diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 35fbd466..00000000 --- a/.editorconfig +++ /dev/null @@ -1,12 +0,0 @@ -[*] -charset=utf-8 -end_of_line=lf -insert_final_newline=true -indent_style=space -indent_size=4 - -[*.json] -indent_size=2 - -[*.{kt,kts}] -disabled_rules=import-ordering,no-wildcard-imports diff --git a/.github/workflows/deploy-store.yml b/.github/workflows/deploy-store.yml index e8a220dd..e7ed6b49 100644 --- a/.github/workflows/deploy-store.yml +++ b/.github/workflows/deploy-store.yml @@ -1,4 +1,4 @@ -name: Deploy release +name: Deploy to app stores on: release: @@ -7,17 +7,16 @@ on: jobs: deploy-google-play: - name: Google Play + name: Deploy to google play runs-on: ubuntu-latest timeout-minutes: 10 environment: google-play steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v2 + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 with: - distribution: 'zulu' - java-version: 17 - - uses: actions/cache@v3 + java-version: 11 + - uses: actions/cache@v2 with: path: | ~/.gradle/caches @@ -29,31 +28,27 @@ jobs: SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }} run: | gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg + gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg - name: Upload apk to google play env: - PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }} PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }} PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }} - ANDROID_PUBLISHER_CREDENTIALS: ${{ secrets.ANDROID_PUBLISHER_CREDENTIALS }} - ADMOB_PROJECT_ID: ${{ secrets.ADMOB_PROJECT_ID }} - SINGLE_SUPPORT_AD_ID: ${{ secrets.SINGLE_SUPPORT_AD_ID }} - DASHBOARD_TILE_AD_ID: ${{ secrets.DASHBOARD_TILE_AD_ID }} - SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }} - run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace; + PLAY_SERVICE_ACCOUNT_EMAIL: ${{ secrets.PLAY_SERVICE_ACCOUNT_EMAIL }} + PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }} + run: ./gradlew publishPlayRelease -PenableFirebase --stacktrace; deploy-app-gallery: - name: AppGallery + name: Deploy to AppGallery runs-on: ubuntu-latest timeout-minutes: 10 environment: app-gallery steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v2 + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 with: - distribution: 'zulu' - java-version: 17 - - uses: actions/cache@v3 + java-version: 11 + - uses: actions/cache@v2 with: path: | ~/.gradle/caches @@ -65,6 +60,7 @@ jobs: SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }} run: | gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/agconnect-services.json.gpg + gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg - name: Prepare credentials env: @@ -72,8 +68,7 @@ jobs: run: echo $AGC_CREDENTIALS > ./app/src/release/agconnect-credentials.json - name: Build and publish HMS version env: - PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }} PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }} PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }} - SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }} - run: ./gradlew bundleHmsRelease --stacktrace && ./gradlew publishHuaweiAppGalleryHmsRelease --stacktrace + PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }} + run: ./gradlew assembleHmsRelease --stacktrace && ./gradlew publishHuaweiAppGalleryHmsRelease --stacktrace diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml index f2e9f016..88edca05 100644 --- a/.github/workflows/deploy-test.yml +++ b/.github/workflows/deploy-test.yml @@ -1,4 +1,4 @@ -name: Deploy DEV +name: Deploy to app tests on: push: @@ -18,12 +18,11 @@ jobs: timeout-minutes: 10 environment: app-center steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v2 + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 with: - distribution: 'zulu' - java-version: 17 - - uses: actions/cache@v3 + java-version: 11 + - uses: actions/cache@v2 with: path: | ~/.gradle/caches @@ -67,7 +66,7 @@ jobs: BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }} run: ./gradlew assembleFdroidDebug --stacktrace - name: Upload apk to github artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v2 with: name: wulkanowyDEV-${{ env.RUN_NUMBER }}.apk path: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk @@ -88,12 +87,11 @@ jobs: environment: app-distribution if: github.event_name != 'pull_request_target' steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v2 + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 with: - distribution: 'zulu' - java-version: 17 - - uses: actions/cache@v3 + java-version: 11 + - uses: actions/cache@v2 with: path: | ~/.gradle/caches @@ -133,7 +131,7 @@ jobs: BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }} run: ./gradlew assemblePlayDebug -PenableFirebase --stacktrace - name: Upload apk to github artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v2 with: name: wulkanowyDEV-${{ env.RUN_NUMBER }}-dev.apk path: app/build/outputs/apk/play/debug/app-play-debug.apk diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bc4b3647..ee16041f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,28 +2,24 @@ name: Tests on: push: - branches: - - master - - develop - - 'hotfix/**' + branches: [ master, develop ] tags: [ '*' ] pull_request: + branches: [ master, develop ] jobs: - - tests-fdroid: - name: F-Droid + unit-tests: + name: Unit tests runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: fkirc/skip-duplicate-actions@master - - uses: actions/checkout@v3 + - uses: actions/checkout@v2 - uses: gradle/wrapper-validation-action@v1 - - uses: actions/setup-java@v2 + - uses: actions/setup-java@v1 with: - distribution: 'zulu' - java-version: 17 - - uses: actions/cache@v3 + java-version: 11 + - uses: actions/cache@v2 with: path: | ~/.gradle/caches @@ -33,58 +29,6 @@ jobs: run: | ./gradlew testFdroidDebugUnitTest --stacktrace ./gradlew jacocoTestReport --stacktrace - - uses: codecov/codecov-action@v3 - with: - flags: unit - - tests-play: - name: Play - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: fkirc/skip-duplicate-actions@master - - uses: actions/checkout@v3 - - uses: gradle/wrapper-validation-action@v1 - - uses: actions/setup-java@v2 - with: - distribution: 'zulu' - java-version: 17 - - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }} - - name: Unit tests - run: | - ./gradlew testPlayDebugUnitTest --stacktrace - ./gradlew jacocoTestReport --stacktrace - - uses: codecov/codecov-action@v3 - with: - flags: unit - - tests-hms: - name: HMS - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: fkirc/skip-duplicate-actions@master - - uses: actions/checkout@v3 - - uses: gradle/wrapper-validation-action@v1 - - uses: actions/setup-java@v2 - with: - distribution: 'zulu' - java-version: 17 - - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }} - - name: Unit tests - run: | - ./gradlew testHmsDebugUnitTest --stacktrace - ./gradlew jacocoTestReport --stacktrace - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v1 with: flags: unit diff --git a/.gitignore b/.gitignore index 921bd0a9..cd5ff714 100644 --- a/.gitignore +++ b/.gitignore @@ -119,4 +119,3 @@ Thumbs.db app/src/release/agconnect-services.json app/src/release/agconnect-credentials.json .idea/deploymentTargetDropDown.xml -.idea/kotlinc.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 1f93faef..ab784474 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -2,6 +2,14 @@ \ No newline at end of file diff --git a/LICENSE b/LICENSE index a1fc3705..5dd9cacf 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2023 Wulkanowy + Copyright 2019 Wulkanowy Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.cs.md b/README.cs.md deleted file mode 100644 index 8171b27d..00000000 --- a/README.cs.md +++ /dev/null @@ -1,73 +0,0 @@ -Česká verze / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md) - -# Wulkanowy - -[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) -[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) -[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) -[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) -[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) - -Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče - -## Funkce - -* přihlášení pomocí emailu a hesla -* funkce z webové stránky deníku: - * známky - * statistiky známek - * frekvence - * procento frekvence - * zkoušky - * plán lekce - * dokončené lekce - * zprávy - * domácí úkoly - * poznámky - * šťastné číslo - * další lekce - * školní setkání - * informace o žáku a škole -* výpočet průměru nezávisle na preferencích školy -* upozornění, např. o nových známkách -* podpora více účtů s možností přejmenování žáků -* tmavý a černý (AMOLED) motiv -* offline režim -* volitelné reklamy na podporu projektu - -## Stáhnout - -Aktuální verzi si můžete stáhnout z Google Play, F-Droid nebo Huawei AppGallery - -[Nyní na Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) -[Stáhnout s F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) -[Objevuj v AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) - -Můžete si také stáhnout [vývojovou verzi](https://wulkanowy.github.io/#download), která zahrnuje nové funkce připravované pro příští vydání - -## Postaveno s pomocí - - -* [Wulkanowy SDK](https://github.com/wulkanowy/sdk) -* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) -* [Hilt](https://dagger.dev/hilt/) -* [Room](https://developer.android.com/topic/libraries/architecture/room) -* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) - -## Spolupráce - -Přispějte do projektu vytvořením PR nebo odesláním issue na GitHub. - -Pro zájemce o překlad aplikace do různých jazyků poskytujeme Crowdin: -https://crowdin.com/project/wulkanowy2 - -## Licence - -Tento projekt je licencován pod licencí Apache License 2.0 - podrobnosti v souboru [LICENSE](LICENSE) diff --git a/README.de.md b/README.de.md deleted file mode 100644 index 972f66ba..00000000 --- a/README.de.md +++ /dev/null @@ -1,73 +0,0 @@ -[Česká verze](README.cs.md) / Deutsche Version / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md) - -# Wulkanowy - -[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) -[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) -[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) -[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) -[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) - -Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre Eltern - -## Merkmale - -* Einloggen mit E-Mail und Passwort -* Funktionen von der Registerwebsite: - * Noten - * Notenstatistik - * Anwesenheit - * Prozentsatz der Anwesenheit - * Prüfungen - * Stundenplan - * abgeschlossene Unterrichtsstunden - * Nachrichten - * Hausaufgaben - * Anmerkungen - * Glückszahl - * Zusätzliche Lektionen - * Schulkonferenzen - * Schüler- und Schulinformationen -* Berechnung des Durchschnitts unabhängig von den Präferenzen der Schule -* Benachrichtigungen, z. B. über eine neue Note -* Unterstützung für mehrere Konten mit der Möglichkeit, den Namen des Schülers zu ändern -* dunkles und schwarzes (AMOLED) Thema -* Offline-Modus -* optionale Werbungen, die es uns ermöglichen das Projekt zu unterstützen - -## Herunterladen - -Die aktuelle Version können Sie von der Google Play, F-Droid oder Huawei AppGallery store herunterladen - -[Get it on Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) -[Get it on F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) -[Explore it on AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) - -Sie können auch eine [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) die beinhaltet neue Funktionen, die für die nächste Version vorbereitet werden - -## Gebaut mit - - -* [Wulkanowy SDK](https://github.com/wulkanowy/sdk) -* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) -* [Hilt](https://dagger.dev/hilt/) -* [Room](https://developer.android.com/topic/libraries/architecture/room) -* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) - -## Beitragen - -Bitte tragen Sie zum Projekt bei, indem Sie entweder eine PR erstellen oder ein Issue auf GitHub einreichen. - -Für Personen, die daran interessiert sind, die Anwendung in verschiedene Sprachen zu übersetzen, bieten wir Crowdin -https://crowdin.com/project/wulkanowy2 - -## Lizenz - -Dieses Projekt ist unter der Apache License 2.0 lizenziert - siehe die [LIZENZ](LICENSE) Datei für Details diff --git a/README.en.md b/README.en.md index 6e4da463..3b6f5bb1 100644 --- a/README.en.md +++ b/README.en.md @@ -1,13 +1,12 @@ -[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / English version / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md) +[Polska wersja README](README.md) # Wulkanowy -[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Unofficial android VULCAN UONET+ register client for both students and their parents @@ -34,7 +33,7 @@ Unofficial android VULCAN UONET+ register client for both students and their par * support for multiple accounts with the ability to rename students * dark and black (AMOLED) theme * offline mode -* optional ads which allow to support the project +* no ads ## Download diff --git a/README.md b/README.md index f3d2e29a..6478ae20 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,12 @@ -[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / Polska wersja / [Slovenská verzia](README.sk.md) +[English version of README](README.en.md) # Wulkanowy -[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica @@ -34,7 +33,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica * obsługa wielu kont wraz z możliwością zmiany nazwy ucznia * ciemny i czarny (AMOLED) motyw * tryb offline -* opcjonalne reklamy umożliwiające wsparcie projektu +* brak reklam ## Pobierz diff --git a/README.sk.md b/README.sk.md deleted file mode 100644 index ff0c6e3c..00000000 --- a/README.sk.md +++ /dev/null @@ -1,73 +0,0 @@ -[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / Slovenská verzia - -# Wulkanowy - -[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) -[![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) -[![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) -[![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) -[![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) - -Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov - -## Funkcie - -* prihlásenie pomocou emailu a hesla -* funkcie z webovej stránky denníka: - * známky - * štatistiky známok - * frekvencia - * percento frekvencie - * skúšky - * plán lekcie - * dokončené lekcie - * správy - * domáce úlohy - * poznámky - * šťastné číslo - * ďalšie lekcie - * školské stretnutie - * informácie o žiakovi a škole -* výpočet priemeru nezávisle od preferencií školy -* upozornenia, napr. o nových známkach -* podpora viacerých účtov s možnosťou premenovania žiakov -* tmavý a čierny (AMOLED) motív -* offline režim -* voliteľné reklamy na podporu projektu - -## Stiahnuť - -Aktuálnu verziu si môžete stiahnuť z Google Play, F-Droid alebo Huawei AppGallery - -[Nyní na Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) -[Stiahnuť s F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) -[Objavíte v AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) - -Môžete si tiež stiahnuť [vývojovú verziu](https://wulkanowy.github.io/#download), ktorá zahrňuje nové funkcie pripravované pre budúce vydanie - -## Postavené s pomocou - - -* [Wulkanowy SDK](https://github.com/wulkanowy/sdk) -* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) -* [Hilt](https://dagger.dev/hilt/) -* [Room](https://developer.android.com/topic/libraries/architecture/room) -* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) - -## Spolupráca - -Prispejte do projektu vytvorením PR alebo odoslaním issue na GitHub. - -Pre záujemcov o preklad aplikácie do rôznych jazykov poskytujeme Crowdin: -https://crowdin.com/project/wulkanowy2 - -## Licencia - -Tento projekt je licencovaný pod licenciou Apache License 2.0 - podrobnosti v súbore [LICENSE](LICENSE) diff --git a/app/build.gradle b/app/build.gradle index 000cc09c..61e47534 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,5 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlinx-serialization' apply plugin: 'kotlin-parcelize' apply plugin: 'kotlin-kapt' apply plugin: 'dagger.hilt.android.plugin' @@ -15,41 +14,32 @@ apply from: 'sonarqube.gradle' apply from: 'hooks.gradle' android { - namespace 'io.github.wulkanowy' - compileSdkVersion 33 + compileSdkVersion 30 defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 - targetSdkVersion 33 - versionCode 130 - versionName "2.0.8" + targetSdkVersion 30 + versionCode 93 + versionName "1.2.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables.useSupportLibrary = true resValue "string", "app_name", "Wulkanowy" + buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis()) manifestPlaceholders = [ - firebase_enabled: project.hasProperty("enableFirebase"), - admob_project_id: "" + firebase_enabled: project.hasProperty("enableFirebase") ] javaCompileOptions { annotationProcessorOptions { arguments += [ - "room.schemaLocation": "$projectDir/schemas".toString(), - "room.incremental" : "true" + "room.schemaLocation": "$projectDir/schemas".toString(), + "room.incremental" : "true" ] } } - - buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null" - buildConfigField "String", "DASHBOARD_TILE_AD_ID", "null" - - if (System.env.SET_BUILD_TIMESTAMP) { - buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis()) - } else { - buildConfigField "long", "BUILD_TIMESTAMP", "1486235849000" - } } sourceSets { @@ -72,16 +62,12 @@ android { shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.release - buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\"" } debug { - minifyEnabled false - shrinkResources false - resValue "string", "app_name", "Wulkanowy DEV" + resValue "string", "app_name", "Wulkanowy DEV " + defaultConfig.versionCode applicationIdSuffix ".dev" versionNameSuffix "-dev" ext.enableCrashlytics = project.hasProperty("enableFirebase") - buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\"" } } @@ -90,44 +76,32 @@ android { productFlavors { hms { dimension "platform" - manifestPlaceholders = [install_channel: "AppGallery"] + manifestPlaceholders = [ + install_channel: "AppGallery" + ] } play { dimension "platform" manifestPlaceholders = [ - install_channel : "Google Play", - admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713" + install_channel: "Google Play" ] - buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "\"${System.getenv("SINGLE_SUPPORT_AD_ID") ?: "ca-app-pub-3940256099942544/5354046379"}\"" - buildConfigField "String", "DASHBOARD_TILE_AD_ID", "\"${System.getenv("DASHBOARD_TILE_AD_ID") ?: "ca-app-pub-3940256099942544/6300978111"}\"" - } fdroid { dimension "platform" - manifestPlaceholders = [install_channel: "F-Droid"] + manifestPlaceholders = [ + install_channel: "F-Droid" + ] } } - playConfigs { - play { enabled.set(true) } - } - buildFeatures { viewBinding true } - bundle { - language { - enableSplit = false - } - } - testOptions.unitTests { includeAndroidResources = true - // workaround HMS test errors https://github.com/robolectric/robolectric/issues/2750 - all { jvmArgs '-noverify' } } compileOptions { @@ -138,14 +112,12 @@ android { kotlinOptions { jvmTarget = "11" - freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"] + freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"] } packagingOptions { - resources { - excludes += ['META-INF/library_release.kotlin_module', - 'META-INF/library-core_release.kotlin_module'] - } + exclude 'META-INF/library_release.kotlin_module' + exclude 'META-INF/library-core_release.kotlin_module' } aboutLibraries { @@ -157,74 +129,62 @@ kapt { correctErrorTypes true } -kotlin { - jvmToolchain(11) -} - play { + serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf" + serviceAccountCredentials = file('key.p12') defaultToAppBundles = false - track = 'production' - releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS - userFraction = 0.25d - updatePriority = 1 - enabled.set(false) + track = 'beta' + updatePriority = 3 } huaweiPublish { instances { hmsRelease { credentialsPath = "$rootDir/app/src/release/agconnect-credentials.json" - buildFormat = "aab" - deployType = "publish" - releaseNotes = [ - new ru.cian.huawei.publish.ReleaseNote( - "pl-PL", - "$projectDir/src/main/play/release-notes/pl-PL/default.txt" - ) - ] + buildFormat = "apk" + deployType = "draft" } } } ext { - work_manager = "2.8.1" + work_manager = "2.5.0" android_hilt = "1.0.0" - room = "2.5.1" + room = "2.3.0" chucker = "3.5.2" - mockk = "1.13.5" - coroutines = "1.7.1" + mockk = "1.12.0" + moshi = "1.12.0" } dependencies { - implementation 'io.github.wulkanowy:sdk:2.0.8' + implementation "io.github.wulkanowy:sdk:1.2.0" - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1" - implementation "androidx.core:core-ktx:1.10.1" - implementation 'androidx.core:core-splashscreen:1.0.1' - implementation "androidx.activity:activity-ktx:1.7.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.core:core-ktx:1.6.0" + implementation "androidx.activity:activity-ktx:1.3.1" + implementation "androidx.appcompat:appcompat:1.3.1" + implementation "androidx.appcompat:appcompat-resources:1.3.1" + implementation "androidx.fragment:fragment-ktx:1.3.6" + implementation "androidx.annotation:annotation:1.2.0" - implementation "androidx.preference:preference-ktx:1.2.0" - implementation "androidx.recyclerview:recyclerview:1.3.0" - implementation "androidx.viewpager2:viewpager2:1.1.0-beta02" + implementation "androidx.preference:preference-ktx:1.1.1" + implementation "androidx.recyclerview:recyclerview:1.2.1" + implementation "androidx.viewpager:viewpager:1.0.0" 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.github.wulkanowy:material-chips-input:2.3.1" + implementation "androidx.constraintlayout:constraintlayout:2.1.0" + implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" + implementation "com.google.android.material:material:1.4.0" + implementation "com.github.wulkanowy:material-chips-input:2.2.0" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" - implementation 'com.github.lopspower:CircularImageView:4.3.0' + implementation 'com.github.lopspower:CircularImageView:4.2.0' implementation "androidx.work:work-runtime-ktx:$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.3.1" implementation "androidx.room:room-runtime:$room" implementation "androidx.room:room-ktx:$room" @@ -238,54 +198,50 @@ dependencies { implementation 'com.github.ncapdevi:FragNav:3.3.0' implementation "com.github.YarikSOffice:lingver:1.3.0" - 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.moshi:moshi:$moshi" + implementation "com.squareup.moshi:moshi-adapters:$moshi" + kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi" 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.0.4' implementation "com.mikepenz:aboutlibraries-core:$about_libraries" - implementation "io.coil-kt:coil:2.4.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 "io.coil-kt:coil:1.3.2" + implementation "io.github.wulkanowy:AppKillerManager:3.0.0" + implementation 'me.xdrop:fuzzywuzzy:1.3.1' + implementation 'com.fredporciuncula:flow-preferences:1.5.0' - playImplementation platform('com.google.firebase:firebase-bom:32.1.0') + playImplementation platform('com.google.firebase:firebase-bom:28.4.0') playImplementation 'com.google.firebase:firebase-analytics-ktx' 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:1.10.0' playImplementation 'com.google.android.play:core-ktx:1.8.1' - playImplementation 'com.google.android.gms:play-services-ads:22.1.0' - 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.1.1.300' + hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.0.300' releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$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' + debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:v1.0.6' testImplementation "junit:junit:4.13.2" testImplementation "io.mockk:mockk:$mockk" - testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines" + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.1' testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - testImplementation 'org.robolectric:robolectric:4.10.3' - testImplementation "androidx.test:runner:1.5.2" - testImplementation "androidx.test.ext:junit:1.1.5" - testImplementation "androidx.test:core:1.5.0" + testImplementation 'org.robolectric:robolectric:4.6.1' + testImplementation "androidx.test:runner:1.4.0" + testImplementation "androidx.test.ext:junit:1.1.3" + testImplementation "androidx.test:core:1.4.0" testImplementation "androidx.room:room-testing:$room" testImplementation "com.google.dagger:hilt-android-testing:$hilt_version" kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version" - androidTestImplementation "androidx.test:core:1.5.0" - androidTestImplementation "androidx.test:runner:1.5.2" - androidTestImplementation "androidx.test.ext:junit:1.1.5" + androidTestImplementation "androidx.test:core:1.4.0" + androidTestImplementation "androidx.test:runner:1.4.0" + androidTestImplementation "androidx.test.ext:junit:1.1.3" androidTestImplementation "io.mockk:mockk-android:$mockk" androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" } diff --git a/app/key.p12.gpg b/app/key.p12.gpg new file mode 100644 index 00000000..e9b6d06e Binary files /dev/null and b/app/key.p12.gpg differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 0fd49f6a..fd948261 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,6 +1,5 @@ # General -dontobfuscate --ignorewarnings #Config for wulkanowy @@ -25,18 +24,3 @@ #Config for Material Components -keep class com.google.android.material.tabs.** { *; } - - -#Config for HMS SDK --keepattributes *Annotation* --keepattributes Exceptions --keepattributes InnerClasses --keepattributes Signature --keep class com.huawei.agconnect.**{*;} --keep class com.huawei.hianalytics.**{*;} --keep class com.huawei.updatesdk.**{*;} --keep class com.huawei.hms.**{*;} - - -#Config for Wulkanowy SDK --keep,allowobfuscation,allowshrinking class retrofit2.Response diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/40.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/40.json deleted file mode 100644 index 362c7f0e..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/40.json +++ /dev/null @@ -1,2316 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 40, - "identityHash": "e2fba6244951713b4e9b217adc5d1a23", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "semester_id" - ], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "oneDriveId", - "columnName": "one_drive_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e2fba6244951713b4e9b217adc5d1a23')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/41.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/41.json deleted file mode 100644 index 9d008060..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/41.json +++ /dev/null @@ -1,2322 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 41, - "identityHash": "d9ce44a78495a358606612bd91603c0f", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "semester_id" - ], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "oneDriveId", - "columnName": "one_drive_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd9ce44a78495a358606612bd91603c0f')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/42.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/42.json deleted file mode 100644 index a5faa57b..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/42.json +++ /dev/null @@ -1,2396 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 42, - "identityHash": "5c8b7f9409294ecdebf9f74a44f8e883", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "semester_id" - ], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "oneDriveId", - "columnName": "one_drive_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5c8b7f9409294ecdebf9f74a44f8e883')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/43.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/43.json deleted file mode 100644 index 22c0d812..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/43.json +++ /dev/null @@ -1,2408 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 43, - "identityHash": "66946510bb620ae82686a5a1a31aba18", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "semester_id" - ], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "oneDriveId", - "columnName": "one_drive_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '66946510bb620ae82686a5a1a31aba18')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/44.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/44.json deleted file mode 100644 index 4dc9834d..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/44.json +++ /dev/null @@ -1,2414 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 44, - "identityHash": "e3437dc0b229a325bbeb3e964a500530", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "semester_id" - ], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "oneDriveId", - "columnName": "one_drive_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e3437dc0b229a325bbeb3e964a500530')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json deleted file mode 100644 index 57f3d431..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json +++ /dev/null @@ -1,2430 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 45, - "identityHash": "f310243440ca00cbc35e62ebaca5c7d8", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "oneDriveId", - "columnName": "one_drive_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f310243440ca00cbc35e62ebaca5c7d8')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json deleted file mode 100644 index 04518141..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json +++ /dev/null @@ -1,2430 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 46, - "identityHash": "f310243440ca00cbc35e62ebaca5c7d8", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "oneDriveId", - "columnName": "one_drive_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f310243440ca00cbc35e62ebaca5c7d8')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json deleted file mode 100644 index 3f8291ea..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json +++ /dev/null @@ -1,2438 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 47, - "identityHash": "ac88c80d4bb923b22f22ce4f91521306", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "oneDriveId", - "columnName": "one_drive_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ac88c80d4bb923b22f22ce4f91521306')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json deleted file mode 100644 index 1c11aae9..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json +++ /dev/null @@ -1,2445 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 48, - "identityHash": "95751b933ad9f835ffc1805f4ef71bdb", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "oneDriveId", - "columnName": "one_drive_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '95751b933ad9f835ffc1805f4ef71bdb')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json deleted file mode 100644 index 5472fb78..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json +++ /dev/null @@ -1,2445 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 49, - "identityHash": "790d4dc0e11f38349c49af85fabf9b7b", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "oneDriveId", - "columnName": "one_drive_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '790d4dc0e11f38349c49af85fabf9b7b')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json deleted file mode 100644 index 4361db95..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json +++ /dev/null @@ -1,2445 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 50, - "identityHash": "87455aae2b15baa976386c833afa9cd9", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "recipient", - "columnName": "recipient_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "removed", - "columnName": "removed", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "oneDriveId", - "columnName": "one_drive_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "ReportingUnits", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "senderId", - "columnName": "sender_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "senderName", - "columnName": "sender_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roles", - "columnName": "roles", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "realName", - "columnName": "real_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginId", - "columnName": "login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "role", - "columnName": "role", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hash", - "columnName": "hash", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '87455aae2b15baa976386c833afa9cd9')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json deleted file mode 100644 index 271b8c90..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json +++ /dev/null @@ -1,2409 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 51, - "identityHash": "51f9cb1d80df003c03bb655c0162487c", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `userLoginId` INTEGER NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "userLoginId", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "globalKey" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '51f9cb1d80df003c03bb655c0162487c')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json deleted file mode 100644 index 129d1917..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json +++ /dev/null @@ -1,2421 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 52, - "identityHash": "8742176f26afcc81279d4a073dca2949", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `userLoginId` INTEGER NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "userLoginId", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "globalKey" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8742176f26afcc81279d4a073dca2949')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json deleted file mode 100644 index 98561787..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json +++ /dev/null @@ -1,2439 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 53, - "identityHash": "1dc96a366125ec9f8567da87cdc9c863", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolId", - "columnName": "schoolId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "globalKey" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1dc96a366125ec9f8567da87cdc9c863')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json deleted file mode 100644 index 7b41672b..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json +++ /dev/null @@ -1,2439 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 54, - "identityHash": "1dc96a366125ec9f8567da87cdc9c863", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolId", - "columnName": "schoolId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "globalKey" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1dc96a366125ec9f8567da87cdc9c863')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/55.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/55.json deleted file mode 100644 index 60c2efbe..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/55.json +++ /dev/null @@ -1,2435 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 55, - "identityHash": "cba22eea6d26cf4d6b9a388ba3329a12", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`message_global_key`, `url`, `filename`))", - "fields": [ - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "message_global_key", - "url", - "filename" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolId", - "columnName": "schoolId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "globalKey" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cba22eea6d26cf4d6b9a388ba3329a12')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/56.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/56.json deleted file mode 100644 index 1a26e717..00000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/56.json +++ /dev/null @@ -1,2442 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 56, - "identityHash": "48f0538bd21601eb5322a7d850e04134", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `scrapper_domain_suffix` TEXT NOT NULL DEFAULT '', `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "scrapperDomainSuffix", - "columnName": "scrapper_domain_suffix", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "''" - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`message_global_key`, `url`, `filename`))", - "fields": [ - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "message_global_key", - "url", - "filename" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolId", - "columnName": "schoolId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "globalKey" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '48f0538bd21601eb5322a7d850e04134')" - ] - } -} \ No newline at end of file diff --git a/app/src/debug/agconnect-services.json b/app/src/debug/agconnect-services.json index 52426f54..48192df0 100644 --- a/app/src/debug/agconnect-services.json +++ b/app/src/debug/agconnect-services.json @@ -1,92 +1,33 @@ { - "agcgw": { - "backurl": "connect-dre.hispace.hicloud.com", - "url": "connect-dre.dbankcloud.cn", - "websocketbackurl": "connect-ws-dre.hispace.dbankcloud.com", - "websocketurl": "connect-ws-dre.hispace.dbankcloud.cn" - }, - "agcgw_all": { - "CN": "connect-drcn.dbankcloud.cn", - "CN_back": "connect-drcn.hispace.hicloud.com", - "DE": "connect-dre.dbankcloud.cn", - "DE_back": "connect-dre.hispace.hicloud.com", - "RU": "connect-drru.hispace.dbankcloud.ru", - "RU_back": "connect-drru.hispace.dbankcloud.cn", - "SG": "connect-dra.dbankcloud.cn", - "SG_back": "connect-dra.hispace.hicloud.com" - }, - "websocketgw_all": { - "CN": "connect-ws-drcn.hispace.dbankcloud.cn", - "CN_back": "connect-ws-drcn.hispace.dbankcloud.com", - "DE": "connect-ws-dre.hispace.dbankcloud.cn", - "DE_back": "connect-ws-dre.hispace.dbankcloud.com", - "RU": "connect-ws-drru.hispace.dbankcloud.ru", - "RU_back": "connect-ws-drru.hispace.dbankcloud.cn", - "SG": "connect-ws-dra.hispace.dbankcloud.cn", - "SG_back": "connect-ws-dra.hispace.dbankcloud.com" - }, - "client": { - "cp_id": "890048000024105546", - "product_id": "736430079244736562", - "client_id": "514530959291319360", - "client_secret": "C42522DBF17D3D4BBE9D9C1783A54484B7E6844B388B7A67502D36A633A4186B", - "project_id": "736430079244736562", - "app_id": "106552551", - "api_key": "CgB6e3x9BUNiq+r8ebCHNojjjYsMT4pJSjjNDOkm9owtBb6rVI6LjnASoZBRxbjjhObcrV5gANo99fI/eKZDTbWS", - "package_name": "io.github.wulkanowy.dev" - }, - "oauth_client": { - "client_id": "106552551", - "client_type": 1 - }, - "app_info": { - "app_id": "106552551", - "package_name": "io.github.wulkanowy.dev" - }, - "service": { - "analytics": { - "collector_url": "datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", - "collector_url_ru": "datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com", - "collector_url_sg": "datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn", - "collector_url_de": "datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", - "collector_url_cn": "datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn", - "resource_id": "p1", - "channel_id": "" - }, + "agcgw":{ + "backurl":"connect-dre.dbankcloud.cn", + "url":"connect-dre.hispace.hicloud.com" + }, + "client":{ + "cp_id":"890048000024105546", + "product_id":"", + "client_id":"", + "client_secret":"", + "app_id":"101440411", + "package_name":"io.github.wulkanowy.dev", + "api_key":"" + }, + "service":{ + "analytics":{ + "collector_url":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", + "resource_id":"p1", + "channel_id":"" + }, "search":{ "url":"https://search-dre.cloud.huawei.com" }, - "cloudstorage": { - "storage_url_sg_back": "https://agc-storage-dra.cloud.huawei.asia", - "storage_url_ru_back": "https://agc-storage-drru.cloud.huawei.ru", - "storage_url_ru": "https://agc-storage-drru.cloud.huawei.ru", - "storage_url_de_back": "https://agc-storage-dre.cloud.huawei.eu", - "storage_url_de": "https://ops-dre.agcstorage.link", - "storage_url": "https://agc-storage-drcn.platform.dbankcloud.cn", - "storage_url_sg": "https://ops-dra.agcstorage.link", - "storage_url_cn_back": "https://agc-storage-drcn.cloud.huawei.com.cn", - "storage_url_cn": "https://agc-storage-drcn.platform.dbankcloud.cn" - }, - "ml": { - "mlservice_url": "ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn" - } - }, - "region": "DE", - "configuration_version": "3.0", - "appInfos": [ - { - "package_name": "io.github.wulkanowy.dev", - "client": { - "app_id": "106552551" - }, - "app_info": { - "package_name": "io.github.wulkanowy.dev", - "app_id": "106552551" - }, - "oauth_client": { - "client_type": 1, - "client_id": "106552551" - } - } - ] + "cloudstorage":{ + "storage_url":"https://ops-dre.agcstorage.link" + }, + "ml":{ + "mlservice_url":"ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn" + } + }, + "region":"DE", + "configuration_version":"1.0" } diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml index 9c21d49d..7dbec2cb 100644 --- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,6 +1,5 @@ - + - - + \ No newline at end of file diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..7dbec2cb --- /dev/null +++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..81e723ec Binary files /dev/null and b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..394b5707 Binary files /dev/null and b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..365b4d66 Binary files /dev/null and b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..463c089b Binary files /dev/null and b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..53d6f5bb Binary files /dev/null and b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt deleted file mode 100644 index 461d2995..00000000 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.utils - -import android.content.Context -import android.view.View -import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.ui.modules.dashboard.DashboardItem -import javax.inject.Inject - -@Suppress("unused") -class AdsHelper @Inject constructor( - @ApplicationContext private val context: Context, - private val preferencesRepository: PreferencesRepository -) { - - fun initialize() { - preferencesRepository.isAdsEnabled = false - preferencesRepository.isAgreeToProcessData = false - preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS - } - - @Suppress("RedundantSuspendModifier", "UNUSED_PARAMETER") - suspend fun getDashboardTileAdBanner(width: Int): AdBanner { - throw IllegalStateException("Can't get ad banner (F-droid)") - } -} - -data class AdBanner(val view: View) diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt index a3eed484..3bf7e169 100644 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -8,7 +8,15 @@ import javax.inject.Singleton @Suppress("UNUSED_PARAMETER") class AnalyticsHelper @Inject constructor() { - fun logEvent(name: String, vararg params: Pair) = Unit - fun setCurrentScreen(activity: Activity, name: String?) = Unit - fun popCurrentScreen(name: String?) = Unit + fun logEvent(name: String, vararg params: Pair) { + // do nothing + } + + fun setCurrentScreen(activity: Activity, name: String?) { + // do nothing + } + + fun popCurrentScreen(name: String?) { + // do nothing + } } diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/InAppReviewHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/InAppReviewHelper.kt index 8615d975..d052b54b 100644 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/InAppReviewHelper.kt +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/InAppReviewHelper.kt @@ -6,7 +6,6 @@ import io.github.wulkanowy.ui.modules.main.MainActivity import javax.inject.Singleton import javax.inject.Inject -@Suppress("UNUSED_PARAMETER", "unused") @Singleton class InAppReviewHelper @Inject constructor( @ApplicationContext private val context: Context diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt deleted file mode 100644 index 88f2598f..00000000 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.utils - -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper() diff --git a/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt deleted file mode 100644 index 0e922702..00000000 --- a/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.utils - -import android.content.Context -import android.view.View -import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.ui.modules.dashboard.DashboardItem -import javax.inject.Inject - -@Suppress("unused") -class AdsHelper @Inject constructor( - @ApplicationContext private val context: Context, - private val preferencesRepository: PreferencesRepository -) { - - fun initialize() { - preferencesRepository.isAdsEnabled = false - preferencesRepository.isAgreeToProcessData = false - preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS - } - - @Suppress("RedundantSuspendModifier", "UNUSED_PARAMETER") - suspend fun getDashboardTileAdBanner(width: Int): AdBanner { - throw IllegalStateException("Can't get ad banner (HMS)") - } -} - -data class AdBanner(val view: View) diff --git a/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt index 1f78931a..5d33825f 100644 --- a/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt +++ b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -3,38 +3,26 @@ package io.github.wulkanowy.utils import android.app.Activity import android.content.Context import android.os.Bundle -import com.huawei.agconnect.crash.AGConnectCrash import com.huawei.hms.analytics.HiAnalytics import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.data.repositories.PreferencesRepository import javax.inject.Inject import javax.inject.Singleton @Singleton class AnalyticsHelper @Inject constructor( - @ApplicationContext private val context: Context, - preferencesRepository: PreferencesRepository, - appInfo: AppInfo, + @ApplicationContext private val context: Context ) { private val analytics by lazy { HiAnalytics.getInstance(context) } - private val connectCrash by lazy { AGConnectCrash.getInstance() } - - init { - if (!appInfo.isDebug) { - connectCrash.setUserId(preferencesRepository.installationId) - } - } - fun logEvent(name: String, vararg params: Pair) { Bundle().apply { - params.forEach { (key, value) -> - if (value == null) return@forEach - when (value) { - is String -> putString(key, value) - is Int -> putInt(key, value) - is Boolean -> putBoolean(key, value) + params.forEach { + if (it.second == null) return@forEach + when (it.second) { + is String, is String? -> putString(it.first, it.second as String) + is Int, is Int? -> putInt(it.first, it.second as Int) + is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean) } } analytics.onEvent(name, this) diff --git a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt index 377e8366..b0c34f41 100644 --- a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt +++ b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt @@ -3,7 +3,6 @@ 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 class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) { @@ -23,10 +22,16 @@ class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR, ExceptionFilter) override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { if (skipLog(priority, tag, message, t)) return + // Disabled due to a bug in the Huawei library + + /*connectCrash.setCustomKey("priority", priority) + connectCrash.setCustomKey("tag", tag.orEmpty()) + connectCrash.setCustomKey("message", message) + if (t != null) { connectCrash.recordException(t) } else { connectCrash.recordException(StackTraceRecorder(format(priority, tag, message))) - } + }*/ } } diff --git a/app/src/hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt index adb162fd..fb9bcae6 100644 --- a/app/src/hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt +++ b/app/src/hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt @@ -7,7 +7,6 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -@Suppress("UNUSED_PARAMETER", "unused") class InAppReviewHelper @Inject constructor( @ApplicationContext private val context: Context ) { @@ -15,4 +14,4 @@ class InAppReviewHelper @Inject constructor( fun showInAppReview(activity: MainActivity) { // do nothing } -} +} \ No newline at end of file diff --git a/app/src/hms/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt deleted file mode 100644 index 88f2598f..00000000 --- a/app/src/hms/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.utils - -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 174c9a1f..a331c41f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ @@ -8,8 +9,6 @@ - - @@ -37,17 +36,15 @@ + android:usesCleartextTraffic="true" + tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"> @@ -72,12 +69,11 @@ android:name=".ui.modules.message.send.SendMessageActivity" android:configChanges="orientation|screenSize" android:label="@string/send_message_title" - android:theme="@style/WulkanowyTheme.NoActionBar" + android:theme="@style/WulkanowyTheme.MessageSend" android:windowSoftInputMode="adjustResize" /> @@ -87,7 +83,6 @@ @@ -98,22 +93,6 @@ - - - - - - - - - - @@ -141,9 +119,11 @@ + + + + android:exported="false" /> - + + + + + + - - diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt index a39a3874..4621c592 100644 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt @@ -1,7 +1,12 @@ package io.github.wulkanowy +import android.annotation.SuppressLint import android.app.Application -import android.util.Log.* +import android.util.Log.DEBUG +import android.util.Log.INFO +import android.util.Log.VERBOSE +import android.webkit.WebView +import androidx.fragment.app.FragmentManager import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import com.yariksoffice.lingver.Lingver @@ -9,7 +14,12 @@ import dagger.hilt.android.HiltAndroidApp import fr.bipi.tressence.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.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 timber.log.Timber import javax.inject.Inject @@ -31,19 +41,14 @@ class WulkanowyApp : Application(), Configuration.Provider { @Inject lateinit var analyticsHelper: AnalyticsHelper - @Inject - lateinit var adsHelper: AdsHelper - - @Inject - lateinit var remoteConfigHelper: RemoteConfigHelper - + @SuppressLint("UnsafeOptInUsageWarning") override fun onCreate() { super.onCreate() + FragmentManager.enableNewStateManager(false) initializeAppLanguage() themeManager.applyDefaultTheme() - adsHelper.initialize() - remoteConfigHelper.initialize() initLogging() + fixWebViewLocale() } private fun initLogging() { @@ -75,6 +80,15 @@ class WulkanowyApp : Application(), Configuration.Provider { } } + private fun fixWebViewLocale() { + //https://stackoverflow.com/questions/40398528/android-webview-language-changes-abruptly-on-android-7-0-and-above + try { + WebView(this).destroy() + } catch (e: Throwable) { + //Ignore exceptions + } + } + override fun getWorkManagerConfiguration() = Configuration.Builder() .setWorkerFactory(workerFactory) .setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO) diff --git a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt similarity index 67% rename from app/src/main/java/io/github/wulkanowy/data/DataModule.kt rename to app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt index c9e4990f..a1c3cbbb 100644 --- a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt @@ -2,99 +2,62 @@ package io.github.wulkanowy.data import android.content.Context import android.content.SharedPreferences +import android.content.res.AssetManager +import android.content.res.Resources import androidx.preference.PreferenceManager import com.chuckerteam.chucker.api.ChuckerCollector import com.chuckerteam.chucker.api.ChuckerInterceptor import com.chuckerteam.chucker.api.RetentionManager +import com.squareup.moshi.Moshi import com.fredporciuncula.flow.preferences.FlowSharedPreferences -import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import dagger.Module import dagger.Provides 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.db.AppDatabase import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AppInfo -import io.github.wulkanowy.utils.RemoteConfigHelper -import kotlinx.serialization.json.Json -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.create +import kotlinx.coroutines.ExperimentalCoroutinesApi import timber.log.Timber -import java.util.concurrent.TimeUnit import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -internal class DataModule { +internal class RepositoryModule { @Singleton @Provides - fun provideSdk(chuckerInterceptor: ChuckerInterceptor, remoteConfig: RemoteConfigHelper) = - Sdk().apply { + fun provideSdk(chuckerCollector: ChuckerCollector, @ApplicationContext context: Context): Sdk { + return Sdk().apply { androidVersion = android.os.Build.VERSION.RELEASE buildTag = android.os.Build.MODEL - userAgentTemplate = remoteConfig.userAgentTemplate setSimpleHttpLogger { Timber.d(it) } // for debug only - addInterceptor(chuckerInterceptor, network = true) + addInterceptor( + ChuckerInterceptor.Builder(context) + .collector(chuckerCollector) + .alwaysReadResponseBody(true) + .build(), network = true + ) } + } @Singleton @Provides fun provideChuckerCollector( @ApplicationContext context: Context, prefRepository: PreferencesRepository - ) = ChuckerCollector( - context = context, - showNotification = prefRepository.isDebugNotificationEnable, - retentionPeriod = RetentionManager.Period.ONE_HOUR - ) - - @Singleton - @Provides - fun provideChuckerInterceptor( - @ApplicationContext context: Context, - chuckerCollector: ChuckerCollector - ) = ChuckerInterceptor.Builder(context) - .collector(chuckerCollector) - .alwaysReadResponseBody(true) - .build() - - @Singleton - @Provides - fun provideOkHttpClient(chuckerInterceptor: ChuckerInterceptor): OkHttpClient = - OkHttpClient.Builder() - .addNetworkInterceptor(chuckerInterceptor) - .addInterceptor(HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.BASIC - }) - .connectTimeout(30, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .build() - - @Singleton - @Provides - fun provideRetrofit( - okHttpClient: OkHttpClient, - json: Json, - appInfo: AppInfo - ): Retrofit = Retrofit.Builder() - .baseUrl(appInfo.messagesBaseUrl) - .client(okHttpClient) - .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) - .build() - - @Singleton - @Provides - fun provideAdminMessageService(retrofit: Retrofit): AdminMessageService = retrofit.create() + ): ChuckerCollector { + return ChuckerCollector( + context = context, + showNotification = prefRepository.isDebugNotificationEnable, + retentionPeriod = RetentionManager.Period.ONE_HOUR + ) + } @Singleton @Provides @@ -104,11 +67,20 @@ internal class DataModule { appInfo: AppInfo ) = AppDatabase.newInstance(context, sharedPrefProvider, appInfo) + @Singleton + @Provides + fun provideResources(@ApplicationContext context: Context): Resources = context.resources + + @Singleton + @Provides + fun provideAssets(@ApplicationContext context: Context): AssetManager = context.assets + @Singleton @Provides fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + @OptIn(ExperimentalCoroutinesApi::class) @Singleton @Provides fun provideFlowSharedPref(sharedPreferences: SharedPreferences) = @@ -116,9 +88,7 @@ internal class DataModule { @Singleton @Provides - fun provideJson() = Json { - ignoreUnknownKeys = true - } + fun provideMoshi() = Moshi.Builder().build() @Singleton @Provides @@ -195,7 +165,7 @@ internal class DataModule { @Singleton @Provides - fun provideMailboxesDao(database: AppDatabase) = database.mailboxDao + fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao @Singleton @Provides @@ -232,12 +202,4 @@ internal class DataModule { @Singleton @Provides fun provideSchoolAnnouncementDao(database: AppDatabase) = database.schoolAnnouncementDao - - @Singleton - @Provides - fun provideNotificationDao(database: AppDatabase) = database.notificationDao - - @Singleton - @Provides - fun provideAdminMessageDao(database: AppDatabase) = database.adminMessagesDao } diff --git a/app/src/main/java/io/github/wulkanowy/data/Resource.kt b/app/src/main/java/io/github/wulkanowy/data/Resource.kt index 6b611e47..406440c8 100644 --- a/app/src/main/java/io/github/wulkanowy/data/Resource.kt +++ b/app/src/main/java/io/github/wulkanowy/data/Resource.kt @@ -1,173 +1,23 @@ package io.github.wulkanowy.data -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import timber.log.Timber - -sealed class Resource { - - open class Loading : Resource() - - data class Intermediate(val data: T) : Loading() - - data class Success(val data: T) : Resource() - - data class Error(val error: Throwable) : Resource() -} - -val Resource.dataOrNull: T? - get() = when (this) { - is Resource.Success -> this.data - is Resource.Intermediate -> this.data - is Resource.Loading -> null - is Resource.Error -> null - } - -val Resource.errorOrNull: Throwable? - get() = when (this) { - is Resource.Error -> this.error - else -> null - } - -fun resourceFlow(block: suspend () -> T) = flow { - emit(Resource.Loading()) - emit(Resource.Success(block())) -}.catch { emit(Resource.Error(it)) } - -fun flatResourceFlow(block: suspend () -> Flow>) = flow { - emit(Resource.Loading()) - emitAll(block().filter { it is Resource.Intermediate || it !is Resource.Loading }) -}.catch { emit(Resource.Error(it)) } - -fun Resource.mapData(block: (T) -> U) = when (this) { - is Resource.Success -> Resource.Success(block(this.data)) - is Resource.Intermediate -> Resource.Intermediate(block(this.data)) - is Resource.Loading -> Resource.Loading() - is Resource.Error -> Resource.Error(this.error) -} - -fun Flow>.logResourceStatus(name: String, showData: Boolean = false) = onEach { - val description = when (it) { - is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else "" - is Resource.Loading -> "started" - is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else "" - is Resource.Error -> "exception occurred: ${it.error}" - } - Timber.i("$name: $description") -} - -fun Flow>.mapResourceData(block: (T) -> U) = map { - it.mapData(block) -} - -fun Flow>.onResourceData(block: suspend (T) -> Unit) = onEach { - when (it) { - is Resource.Success -> block(it.data) - is Resource.Intermediate -> block(it.data) - is Resource.Error, - is Resource.Loading -> Unit - } -} - -fun Flow>.onResourceLoading(block: suspend () -> Unit) = onEach { - if (it is Resource.Loading) { - block() - } -} - -fun Flow>.onResourceIntermediate(block: suspend (T) -> Unit) = onEach { - if (it is Resource.Intermediate) { - block(it.data) - } -} - -fun Flow>.onResourceSuccess(block: suspend (T) -> Unit) = onEach { - if (it is Resource.Success) { - block(it.data) - } -} - -fun Flow>.onResourceError(block: (Throwable) -> Unit) = onEach { - if (it is Resource.Error) { - block(it.error) - } -} - -fun Flow>.onResourceNotLoading(block: () -> Unit) = onEach { - if (it !is Resource.Loading) { - block() - } -} - -suspend fun Flow>.toFirstResult() = filter { it !is Resource.Loading }.first() - -suspend fun Flow>.waitForResult() = takeWhile { it is Resource.Loading }.collect() - -inline fun networkBoundResource( - mutex: Mutex = Mutex(), - showSavedOnLoading: Boolean = true, - crossinline isResultEmpty: (ResultType) -> Boolean, - crossinline query: () -> Flow, - crossinline fetch: suspend (ResultType) -> RequestType, - crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit, - crossinline onFetchFailed: (Throwable) -> Unit = { }, - crossinline shouldFetch: (ResultType) -> Boolean = { true }, - crossinline filterResult: (ResultType) -> ResultType = { it } -) = flow { - emit(Resource.Loading()) - - val data = query().first() - emitAll(if (shouldFetch(data)) { - val filteredResult = filterResult(data) - - if (showSavedOnLoading && !isResultEmpty(filteredResult)) { - emit(Resource.Intermediate(filteredResult)) +data class Resource(val status: Status, val data: T?, val error: Throwable?) { + companion object { + fun success(data: T?): Resource { + return Resource(Status.SUCCESS, data, null) } - try { - val newData = fetch(data) - mutex.withLock { saveFetchResult(query().first(), newData) } - query().map { Resource.Success(filterResult(it)) } - } catch (throwable: Throwable) { - onFetchFailed(throwable) - query().map { Resource.Error(throwable) } + fun error(error: Throwable?, data: T? = null): Resource { + return Resource(Status.ERROR, data, error) } - } else { - query().map { Resource.Success(filterResult(it)) } - }) + + fun loading(data: T? = null): Resource { + return Resource(Status.LOADING, data, null) + } + } } -@JvmName("networkBoundResourceWithMap") -inline fun networkBoundResource( - mutex: Mutex = Mutex(), - showSavedOnLoading: Boolean = true, - crossinline isResultEmpty: (T) -> Boolean, - crossinline query: () -> Flow, - crossinline fetch: suspend (ResultType) -> RequestType, - crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit, - crossinline onFetchFailed: (Throwable) -> Unit = { }, - crossinline shouldFetch: (ResultType) -> Boolean = { true }, - crossinline mapResult: (ResultType) -> T -) = flow { - emit(Resource.Loading()) - - val data = query().first() - emitAll(if (shouldFetch(data)) { - val mappedResult = mapResult(data) - - if (showSavedOnLoading && !isResultEmpty(mappedResult)) { - emit(Resource.Intermediate(mappedResult)) - } - try { - val newData = fetch(data) - mutex.withLock { saveFetchResult(query().first(), newData) } - query().map { Resource.Success(mapResult(it)) } - } catch (throwable: Throwable) { - onFetchFailed(throwable) - query().map { Resource.Error(throwable) } - } - } else { - query().map { Resource.Success(mapResult(it)) } - }) +enum class Status { + LOADING, + SUCCESS, + ERROR } diff --git a/app/src/main/java/io/github/wulkanowy/data/api/AdminMessageService.kt b/app/src/main/java/io/github/wulkanowy/data/api/AdminMessageService.kt deleted file mode 100644 index 23f5af24..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/api/AdminMessageService.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.wulkanowy.data.api - -import io.github.wulkanowy.data.db.entities.AdminMessage -import retrofit2.http.GET -import javax.inject.Singleton - -@Singleton -interface AdminMessageService { - - @GET("/v1.json") - suspend fun getAdminMessages(): List -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 882a7016..0dca9aa1 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -1,11 +1,105 @@ package io.github.wulkanowy.data.db import android.content.Context -import androidx.room.* +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase import androidx.room.RoomDatabase.JournalMode.TRUNCATE -import io.github.wulkanowy.data.db.dao.* -import io.github.wulkanowy.data.db.entities.* -import io.github.wulkanowy.data.db.migrations.* +import androidx.room.TypeConverters +import io.github.wulkanowy.data.db.dao.AttendanceDao +import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao +import io.github.wulkanowy.data.db.dao.CompletedLessonsDao +import io.github.wulkanowy.data.db.dao.ConferenceDao +import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao +import io.github.wulkanowy.data.db.dao.ExamDao +import io.github.wulkanowy.data.db.dao.GradeDao +import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao +import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao +import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao +import io.github.wulkanowy.data.db.dao.GradeSummaryDao +import io.github.wulkanowy.data.db.dao.HomeworkDao +import io.github.wulkanowy.data.db.dao.LuckyNumberDao +import io.github.wulkanowy.data.db.dao.MessageAttachmentDao +import io.github.wulkanowy.data.db.dao.MessagesDao +import io.github.wulkanowy.data.db.dao.MobileDeviceDao +import io.github.wulkanowy.data.db.dao.NoteDao +import io.github.wulkanowy.data.db.dao.RecipientDao +import io.github.wulkanowy.data.db.dao.ReportingUnitDao +import io.github.wulkanowy.data.db.dao.SchoolDao +import io.github.wulkanowy.data.db.dao.SemesterDao +import io.github.wulkanowy.data.db.dao.StudentDao +import io.github.wulkanowy.data.db.dao.StudentInfoDao +import io.github.wulkanowy.data.db.dao.SubjectDao +import io.github.wulkanowy.data.db.dao.TeacherDao +import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao +import io.github.wulkanowy.data.db.dao.TimetableDao +import io.github.wulkanowy.data.db.dao.TimetableHeaderDao +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.db.entities.AttendanceSummary +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.GradePartialStatistics +import io.github.wulkanowy.data.db.entities.GradePointsStatistics +import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageAttachment +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.School +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentInfo +import io.github.wulkanowy.data.db.entities.Subject +import io.github.wulkanowy.data.db.entities.Teacher +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.data.db.entities.TimetableHeader +import io.github.wulkanowy.data.db.migrations.Migration10 +import io.github.wulkanowy.data.db.migrations.Migration11 +import io.github.wulkanowy.data.db.migrations.Migration12 +import io.github.wulkanowy.data.db.migrations.Migration13 +import io.github.wulkanowy.data.db.migrations.Migration14 +import io.github.wulkanowy.data.db.migrations.Migration15 +import io.github.wulkanowy.data.db.migrations.Migration16 +import io.github.wulkanowy.data.db.migrations.Migration17 +import io.github.wulkanowy.data.db.migrations.Migration18 +import io.github.wulkanowy.data.db.migrations.Migration19 +import io.github.wulkanowy.data.db.migrations.Migration2 +import io.github.wulkanowy.data.db.migrations.Migration20 +import io.github.wulkanowy.data.db.migrations.Migration21 +import io.github.wulkanowy.data.db.migrations.Migration22 +import io.github.wulkanowy.data.db.migrations.Migration23 +import io.github.wulkanowy.data.db.migrations.Migration24 +import io.github.wulkanowy.data.db.migrations.Migration25 +import io.github.wulkanowy.data.db.migrations.Migration26 +import io.github.wulkanowy.data.db.migrations.Migration27 +import io.github.wulkanowy.data.db.migrations.Migration28 +import io.github.wulkanowy.data.db.migrations.Migration29 +import io.github.wulkanowy.data.db.migrations.Migration3 +import io.github.wulkanowy.data.db.migrations.Migration30 +import io.github.wulkanowy.data.db.migrations.Migration31 +import io.github.wulkanowy.data.db.migrations.Migration32 +import io.github.wulkanowy.data.db.migrations.Migration33 +import io.github.wulkanowy.data.db.migrations.Migration34 +import io.github.wulkanowy.data.db.migrations.Migration35 +import io.github.wulkanowy.data.db.migrations.Migration36 +import io.github.wulkanowy.data.db.migrations.Migration37 +import io.github.wulkanowy.data.db.migrations.Migration38 +import io.github.wulkanowy.data.db.migrations.Migration39 +import io.github.wulkanowy.data.db.migrations.Migration4 +import io.github.wulkanowy.data.db.migrations.Migration5 +import io.github.wulkanowy.data.db.migrations.Migration6 +import io.github.wulkanowy.data.db.migrations.Migration7 +import io.github.wulkanowy.data.db.migrations.Migration8 +import io.github.wulkanowy.data.db.migrations.Migration9 import io.github.wulkanowy.utils.AppInfo import javax.inject.Singleton @@ -30,7 +124,7 @@ import javax.inject.Singleton Subject::class, LuckyNumber::class, CompletedLesson::class, - Mailbox::class, + ReportingUnit::class, Recipient::class, MobileDevice::class, Teacher::class, @@ -40,16 +134,6 @@ import javax.inject.Singleton StudentInfo::class, TimetableHeader::class, SchoolAnnouncement::class, - Notification::class, - AdminMessage::class - ], - autoMigrations = [ - AutoMigration(from = 44, to = 45), - AutoMigration(from = 46, to = 47), - AutoMigration(from = 47, to = 48), - AutoMigration(from = 51, to = 52), - AutoMigration(from = 54, to = 55, spec = Migration55::class), - AutoMigration(from = 55, to = 56), ], version = AppDatabase.VERSION_SCHEMA, exportSchema = true @@ -58,7 +142,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 56 + const val VERSION_SCHEMA = 39 fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( Migration2(), @@ -99,17 +183,6 @@ abstract class AppDatabase : RoomDatabase() { Migration37(), Migration38(), Migration39(), - Migration40(), - Migration41(sharedPrefProvider), - Migration42(), - Migration43(), - Migration44(), - Migration46(), - Migration49(), - Migration50(), - Migration51(), - Migration53(), - Migration54(), ) fun newInstance( @@ -160,7 +233,7 @@ abstract class AppDatabase : RoomDatabase() { abstract val completedLessonsDao: CompletedLessonsDao - abstract val mailboxDao: MailboxDao + abstract val reportingUnitDao: ReportingUnitDao abstract val recipientDao: RecipientDao @@ -179,8 +252,4 @@ abstract class AppDatabase : RoomDatabase() { abstract val timetableHeaderDao: TimetableHeaderDao abstract val schoolAnnouncementDao: SchoolAnnouncementDao - - abstract val notificationDao: NotificationDao - - abstract val adminMessagesDao: AdminMessageDao } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt index 9d3beae1..def0b371 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt @@ -1,36 +1,47 @@ package io.github.wulkanowy.data.db import androidx.room.TypeConverter -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.utils.toTimestamp -import kotlinx.serialization.SerializationException -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import java.time.* -import java.util.* +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import io.github.wulkanowy.data.db.adapters.PairAdapterFactory import java.time.Instant import java.time.LocalDate +import java.time.LocalDateTime import java.time.Month import java.time.ZoneOffset -import java.util.* +import java.util.Date class Converters { - private val json = Json + private val moshi by lazy { Moshi.Builder().add(PairAdapterFactory).build() } + + private val integerListAdapter by lazy { + moshi.adapter>(Types.newParameterizedType(List::class.java, Integer::class.java)) + } + + private val stringListPairAdapter by lazy { + moshi.adapter>>(Types.newParameterizedType(List::class.java, Pair::class.java, String::class.java, String::class.java)) + } @TypeConverter - fun timestampToLocalDate(value: Long?): LocalDate? = - value?.let(::Date)?.toInstant()?.atZone(ZoneOffset.UTC)?.toLocalDate() + fun timestampToDate(value: Long?): LocalDate? = value?.run { + Date(value).toInstant().atZone(ZoneOffset.UTC).toLocalDate() + } @TypeConverter - fun dateToTimestamp(date: LocalDate?): Long? = date?.toTimestamp() + fun dateToTimestamp(date: LocalDate?): Long? { + return date?.atStartOfDay()?.toInstant(ZoneOffset.UTC)?.toEpochMilli() + } @TypeConverter - fun instantToTimestamp(instant: Instant?): Long? = instant?.toEpochMilli() + fun timestampToTime(value: Long?): LocalDateTime? = value?.let { + LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC) + } @TypeConverter - fun timestampToInstant(timestamp: Long?): Instant? = timestamp?.let(Instant::ofEpochMilli) + fun timeToTimestamp(date: LocalDateTime?): Long? { + return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli() + } @TypeConverter fun monthToInt(month: Month?) = month?.value @@ -40,32 +51,21 @@ class Converters { @TypeConverter fun intListToJson(list: List): String { - return json.encodeToString(list) + return integerListAdapter.toJson(list) } @TypeConverter fun jsonToIntList(value: String): List { - return json.decodeFromString(value) + return integerListAdapter.fromJson(value).orEmpty() } @TypeConverter fun stringPairListToJson(list: List>): String { - return json.encodeToString(list) + return stringListPairAdapter.toJson(list) } @TypeConverter fun jsonToStringPairList(value: String): List> { - return try { - json.decodeFromString(value) - } catch (e: SerializationException) { - emptyList() // handle errors from old gson Pair serialized data - } + return stringListPairAdapter.fromJson(value).orEmpty() } - - @TypeConverter - fun destinationToString(destination: Destination) = json.encodeToString(destination) - - @TypeConverter - fun stringToDestination(destination: String): Destination = json.decodeFromString(destination) - } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt index 4929f046..0623d403 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt @@ -22,14 +22,11 @@ class SharedPrefProvider @Inject constructor( fun getString(key: String) = sharedPref.getString(key, null) - fun getString(key: String, defaultValue: String): String = - sharedPref.getString(key, defaultValue) ?: defaultValue + fun getString(key: String, defaultValue: String): String = sharedPref.getString(key, defaultValue) ?: defaultValue - fun getBoolean(key: String, defaultValue: Boolean): Boolean = - sharedPref.getBoolean(key, defaultValue) + fun getBoolean(key: String, defaultValue: Boolean): Boolean = sharedPref.getBoolean(key, defaultValue) - fun putBoolean(key: String, value: Boolean, sync: Boolean = false) = - sharedPref.edit(sync) { putBoolean(key, value) } + fun putBoolean(key: String, value: Boolean, sync: Boolean = false) = sharedPref.edit(sync) { putBoolean(key, value) } fun putString(key: String, value: String?, sync: Boolean = false) { sharedPref.edit(sync) { putString(key, value) } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/adapters/PairAdapterFactory.kt b/app/src/main/java/io/github/wulkanowy/data/db/adapters/PairAdapterFactory.kt new file mode 100644 index 00000000..4a9b168d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/adapters/PairAdapterFactory.kt @@ -0,0 +1,68 @@ +package io.github.wulkanowy.data.db.adapters + +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonReader +import com.squareup.moshi.JsonWriter +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types +import java.lang.reflect.ParameterizedType +import java.lang.reflect.Type + +object PairAdapterFactory : JsonAdapter.Factory { + + override fun create(type: Type, annotations: MutableSet, moshi: Moshi): JsonAdapter<*>? { + if (type !is ParameterizedType || List::class.java != type.rawType) return null + if (type.actualTypeArguments[0] != Pair::class.java) return null + + val listType = Types.newParameterizedType(List::class.java, Map::class.java, String::class.java) + val listAdapter = moshi.adapter>>(listType) + + val mapType = Types.newParameterizedType(MutableMap::class.java, String::class.java, String::class.java) + val mapAdapter = moshi.adapter>(mapType) + + return PairAdapter(listAdapter, mapAdapter) + } + + private class PairAdapter( + private val listAdapter: JsonAdapter>>, + private val mapAdapter: JsonAdapter>, + ) : JsonAdapter>>() { + + override fun toJson(writer: JsonWriter, value: List>?) { + writer.beginArray() + value?.forEach { + writer.beginObject() + writer.name("first").value(it.first) + writer.name("second").value(it.second) + writer.endObject() + } + writer.endArray() + } + + override fun fromJson(reader: JsonReader): List>? { + return if (reader.peek() == JsonReader.Token.BEGIN_OBJECT) deserializeMoshiMap(reader) + else deserializeGsonPair(reader) + } + + // for compatibility with 0.21.0 + private fun deserializeMoshiMap(reader: JsonReader): List>? { + val map = mapAdapter.fromJson(reader) ?: return null + + return map.entries.map { + it.key to it.value + } + } + + private fun deserializeGsonPair(reader: JsonReader): List>? { + val list = listAdapter.fromJson(reader) ?: return null + + return list.map { + require(it.size == 2) { + "pair with more or less than two elements: $list" + } + + it["first"].orEmpty() to it["second"].orEmpty() + } + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AdminMessageDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AdminMessageDao.kt deleted file mode 100644 index 87f4812d..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/AdminMessageDao.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Query -import androidx.room.Transaction -import io.github.wulkanowy.data.db.entities.AdminMessage -import kotlinx.coroutines.flow.Flow -import javax.inject.Singleton - -@Singleton -@Dao -abstract class AdminMessageDao : BaseDao { - - @Query("SELECT * FROM AdminMessages") - abstract fun loadAll(): Flow> - - @Transaction - open suspend fun removeOldAndSaveNew( - oldMessages: List, - newMessages: List - ) { - deleteAll(oldMessages) - insertAll(newMessages) - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt index c6c255a1..8ef3fd44 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/AttendanceDao.kt @@ -11,11 +11,6 @@ import javax.inject.Singleton @Dao interface AttendanceDao : BaseDao { - @Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :start AND date <= :end") - fun loadAll( - diaryId: Int, - studentId: Int, - start: LocalDate, - end: LocalDate - ): Flow> + @Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") + fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt index 056a5cbd..048e9e3c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt @@ -2,12 +2,11 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Delete import androidx.room.Insert -import androidx.room.OnConflictStrategy import androidx.room.Update interface BaseDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) + @Insert suspend fun insertAll(items: List): List @Update diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt index ca9da9ea..e84bad59 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt @@ -4,7 +4,7 @@ import androidx.room.Dao import androidx.room.Query import io.github.wulkanowy.data.db.entities.Conference import kotlinx.coroutines.flow.Flow -import java.time.Instant +import java.time.LocalDateTime import javax.inject.Singleton @Dao @@ -12,5 +12,5 @@ import javax.inject.Singleton interface ConferenceDao : BaseDao { @Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :startDate") - fun loadAll(diaryId: Int, studentId: Int, startDate: Instant): Flow> + fun loadAll(diaryId: Int, studentId: Int, startDate: LocalDateTime): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt deleted file mode 100644 index 084192a0..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Mailbox -import kotlinx.coroutines.flow.Flow -import javax.inject.Singleton - -@Singleton -@Dao -interface MailboxDao : BaseDao { - - @Query("SELECT * FROM Mailboxes WHERE email = :email") - suspend fun loadAll(email: String): List - - @Query("SELECT * FROM Mailboxes WHERE email = :email AND symbol = :symbol AND schoolId = :schoolId") - fun loadAll(email: String, symbol: String, schoolId: String): Flow> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt index 1709f763..729ba6a6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt @@ -11,12 +11,9 @@ import kotlinx.coroutines.flow.Flow interface MessagesDao : BaseDao { @Transaction - @Query("SELECT * FROM Messages WHERE message_global_key = :messageGlobalKey") - fun loadMessageWithAttachment(messageGlobalKey: String): Flow + @Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId") + fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow - @Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC") - fun loadAll(mailboxKey: String, folder: Int): Flow> - - @Query("SELECT * FROM Messages WHERE email = :email AND folder_id = :folder ORDER BY date DESC") - fun loadAll(folder: Int, email: String): Flow> + @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC") + fun loadAll(studentId: Int, folder: Int): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt index 96382cc1..081e859a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt @@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow @Dao interface MobileDeviceDao : BaseDao { - @Query("SELECT * FROM MobileDevices WHERE user_login_id = :userLoginId ORDER BY date DESC") + @Query("SELECT * FROM MobileDevices WHERE student_id = :userLoginId ORDER BY date DESC") fun loadAll(userLoginId: Int): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/NotificationDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/NotificationDao.kt deleted file mode 100644 index c5ae21bc..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/NotificationDao.kt +++ /dev/null @@ -1,15 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.Notification -import kotlinx.coroutines.flow.Flow -import javax.inject.Singleton - -@Singleton -@Dao -interface NotificationDao : BaseDao { - - @Query("SELECT * FROM Notifications WHERE student_id = :studentId OR student_id = -1") - fun loadAll(studentId: Long): Flow> -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt index 1956261e..c2787ac3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt @@ -2,7 +2,6 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao import androidx.room.Query -import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Recipient import javax.inject.Singleton @@ -10,6 +9,6 @@ import javax.inject.Singleton @Dao interface RecipientDao : BaseDao { - @Query("SELECT * FROM Recipients WHERE type = :type AND studentMailboxGlobalKey = :studentMailboxGlobalKey") - suspend fun loadAll(type: MailboxType, studentMailboxGlobalKey: String): List + @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND unit_id = :unitId AND role = :role") + suspend fun loadAll(studentId: Int, unitId: Int, role: Int): List } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt new file mode 100644 index 00000000..ca697eda --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.ReportingUnit +import javax.inject.Singleton + +@Singleton +@Dao +interface ReportingUnitDao : BaseDao { + + @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId") + suspend fun load(studentId: Int): List + + @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId") + suspend fun loadOne(studentId: Int, unitId: Int): ReportingUnit? +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt index c32e4aba..56806604 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt @@ -10,6 +10,6 @@ import javax.inject.Singleton @Singleton interface SchoolAnnouncementDao : BaseDao { - @Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :userLoginId ORDER BY date DESC") - fun loadAll(userLoginId: Int): Flow> + @Query("SELECT * FROM SchoolAnnouncements WHERE student_id = :studentId") + fun loadAll(studentId: Int): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt index a2f0abac..0ad2ee59 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt @@ -1,54 +1,46 @@ package io.github.wulkanowy.data.db.dao -import androidx.room.* +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy.ABORT +import androidx.room.Query +import androidx.room.Transaction +import androidx.room.Update import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.StudentName import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar import io.github.wulkanowy.data.db.entities.StudentWithSemesters import javax.inject.Singleton @Singleton @Dao -abstract class StudentDao { +interface StudentDao { - @Insert(onConflict = OnConflictStrategy.ABORT) - abstract suspend fun insertAll(student: List): List + @Insert(onConflict = ABORT) + suspend fun insertAll(student: List): List @Delete - abstract suspend fun delete(student: Student) + suspend fun delete(student: Student) @Update(entity = Student::class) - abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar) - - @Update(entity = Student::class) - abstract suspend fun update(studentName: StudentName) + suspend fun update(studentNickAndAvatar: StudentNickAndAvatar) @Query("SELECT * FROM Students WHERE is_current = 1") - abstract suspend fun loadCurrent(): Student? + suspend fun loadCurrent(): Student? @Query("SELECT * FROM Students WHERE id = :id") - abstract suspend fun loadById(id: Long): Student? + suspend fun loadById(id: Long): Student? @Query("SELECT * FROM Students") - abstract suspend fun loadAll(): List + suspend fun loadAll(): List @Transaction @Query("SELECT * FROM Students") - abstract suspend fun loadStudentsWithSemesters(): List - - @Transaction - @Query("SELECT * FROM Students WHERE id = :id") - abstract suspend fun loadStudentWithSemestersById(id: Long): StudentWithSemesters? + suspend fun loadStudentsWithSemesters(): List @Query("UPDATE Students SET is_current = 1 WHERE id = :id") - abstract suspend fun updateCurrent(id: Long) + suspend fun updateCurrent(id: Long) @Query("UPDATE Students SET is_current = 0") - abstract suspend fun resetCurrent() - - @Transaction - open suspend fun switchCurrent(id: Long) { - resetCurrent() - updateCurrent(id) - } + suspend fun resetCurrent() } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableAdditionalDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableAdditionalDao.kt index 914ce340..335e003e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableAdditionalDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableAdditionalDao.kt @@ -5,7 +5,6 @@ import androidx.room.Query import io.github.wulkanowy.data.db.entities.TimetableAdditional import kotlinx.coroutines.flow.Flow import java.time.LocalDate -import java.util.UUID import javax.inject.Singleton @Dao @@ -13,13 +12,5 @@ import javax.inject.Singleton interface TimetableAdditionalDao : BaseDao { @Query("SELECT * FROM TimetableAdditional WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll( - diaryId: Int, - studentId: Int, - from: LocalDate, - end: LocalDate - ): Flow> - - @Query("DELETE FROM TimetableAdditional WHERE repeat_id = :repeatId") - suspend fun deleteAllByRepeatId(repeatId: UUID) + fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt index b4b7379f..5e6eec66 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt @@ -13,7 +13,4 @@ interface TimetableDao : BaseDao { @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow> - - @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/AdminMessage.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/AdminMessage.kt deleted file mode 100644 index 97fec69b..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/AdminMessage.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import kotlinx.serialization.Serializable - -@Serializable -@Entity(tableName = "AdminMessages") -data class AdminMessage( - - @PrimaryKey - val id: Int, - - val title: String, - - val content: String, - - @ColumnInfo(name = "version_name") - val versionMin: Int? = null, - - @ColumnInfo(name = "version_max") - val versionMax: Int? = null, - - @ColumnInfo(name = "target_register_host") - val targetRegisterHost: String? = null, - - @ColumnInfo(name = "target_flavor") - val targetFlavor: String? = null, - - @ColumnInfo(name = "destination_url") - val destinationUrl: String? = null, - - val priority: String, - - val type: String, - - @ColumnInfo(name = "is_dismissible") - val isDismissible: Boolean = false -) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt index b40dd52e..f141d5d5 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt @@ -47,7 +47,4 @@ data class Attendance( @PrimaryKey(autoGenerate = true) var id: Long = 0 - - @ColumnInfo(name = "is_notified") - var isNotified: Boolean = true } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt index ba3958db..4ad94650 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt @@ -4,7 +4,7 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import java.io.Serializable -import java.time.Instant +import java.time.LocalDateTime @Entity(tableName = "Conferences") data class Conference( @@ -27,7 +27,7 @@ data class Conference( @ColumnInfo(name = "conference_id") val conferenceId: Int, - val date: Instant, + val date: LocalDateTime ) : Serializable { @PrimaryKey(autoGenerate = true) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt index 2292c3e6..50299e60 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt @@ -22,7 +22,6 @@ data class Exam( val subject: String, - @Deprecated("not available anymore") val group: String, val type: String, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt index 9e08b86b..e747271c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt @@ -24,8 +24,5 @@ data class GradeSemesterStatistics( var id: Long = 0 @Transient - var classAverage: String = "" - - @Transient - var studentAverage: String = "" + var average: String = "" } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt index a42832ce..fb7b60bb 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt @@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.entities import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey -import java.time.Instant +import java.time.LocalDateTime @Entity(tableName = "GradesSummary") data class GradeSummary( @@ -45,8 +45,8 @@ data class GradeSummary( var isFinalGradeNotified: Boolean = true @ColumnInfo(name = "predicted_grade_last_change") - var predictedGradeLastChange: Instant = Instant.now() + var predictedGradeLastChange: LocalDateTime = LocalDateTime.now() @ColumnInfo(name = "final_grade_last_change") - var finalGradeLastChange: Instant = Instant.now() + var finalGradeLastChange: LocalDateTime = LocalDateTime.now() } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt index 4538cf31..04ee1e8c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt @@ -40,7 +40,4 @@ data class Homework( @ColumnInfo(name = "is_notified") var isNotified: Boolean = true - - @ColumnInfo(name = "is_added_by_user") - var isAddedByUser: Boolean = false } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt deleted file mode 100644 index e65e213d..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import android.os.Parcelable -import androidx.room.Entity -import androidx.room.PrimaryKey -import kotlinx.parcelize.Parcelize - -@Parcelize -@Entity(tableName = "Mailboxes") -data class Mailbox( - - @PrimaryKey - val globalKey: String, - - val email: String, - val symbol: String, - val schoolId: String, - - val fullName: String, - val userName: String, - val studentName: String, - val schoolNameShort: String, - val type: MailboxType, -) : java.io.Serializable, Parcelable - -enum class MailboxType { - STUDENT, - PARENT, - GUARDIAN, - EMPLOYEE, - UNKNOWN, -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt index d1356b33..7b6e0dbf 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt @@ -4,39 +4,39 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import java.io.Serializable -import java.time.Instant +import java.time.LocalDateTime @Entity(tableName = "Messages") data class Message( - @ColumnInfo(name = "email") - val email: String, + @ColumnInfo(name = "student_id") + val studentId: Long, - @ColumnInfo(name = "message_global_key") - val messageGlobalKey: String, - - @ColumnInfo(name = "mailbox_key") - val mailboxKey: String, + @ColumnInfo(name = "real_id") + val realId: Int, @ColumnInfo(name = "message_id") val messageId: Int, - val correspondents: String, + @ColumnInfo(name = "sender_name") + val sender: String, + + @ColumnInfo(name = "sender_id") + val senderId: Int, + + @ColumnInfo(name = "recipient_name") + val recipient: String, val subject: String, - val date: Instant, + val date: LocalDateTime, @ColumnInfo(name = "folder_id") val folderId: Int, var unread: Boolean, - @ColumnInfo(name = "read_by") - val readBy: Int?, - - @ColumnInfo(name = "unread_by") - val unreadBy: Int?, + val removed: Boolean, @ColumnInfo(name = "has_attachments") val hasAttachments: Boolean @@ -48,7 +48,11 @@ data class Message( @ColumnInfo(name = "is_notified") var isNotified: Boolean = true + @ColumnInfo(name = "unread_by") + var unreadBy: Int = 0 + + @ColumnInfo(name = "read_by") + var readBy: Int = 0 + var content: String = "" - var sender: String? = null - var recipients: String? = null } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt index 6f0c84ad..d1886e91 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt @@ -2,16 +2,21 @@ package io.github.wulkanowy.data.db.entities import androidx.room.ColumnInfo import androidx.room.Entity +import androidx.room.PrimaryKey import java.io.Serializable -@Entity( - tableName = "MessageAttachments", - primaryKeys = ["message_global_key", "url", "filename"], -) +@Entity(tableName = "MessageAttachments") data class MessageAttachment( - @ColumnInfo(name = "message_global_key") - val messageGlobalKey: String, + @PrimaryKey + @ColumnInfo(name = "real_id") + val realId: Int, + + @ColumnInfo(name = "message_id") + val messageId: Int, + + @ColumnInfo(name = "one_drive_id") + val oneDriveId: String, @ColumnInfo(name = "url") val url: String, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt index cd468215..2e7af0f4 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt @@ -7,6 +7,6 @@ data class MessageWithAttachment( @Embedded val message: Message, - @Relation(parentColumn = "message_global_key", entityColumn = "message_global_key") + @Relation(parentColumn = "message_id", entityColumn = "message_id") val attachments: List ) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt index 89b04ccc..83d82c0b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt @@ -4,12 +4,12 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import java.io.Serializable -import java.time.Instant +import java.time.LocalDateTime @Entity(tableName = "MobileDevices") data class MobileDevice( - @ColumnInfo(name = "user_login_id") + @ColumnInfo(name = "student_id") val userLoginId: Int, @ColumnInfo(name = "device_id") @@ -17,7 +17,7 @@ data class MobileDevice( val name: String, - val date: Instant, + val date: LocalDateTime ) : Serializable { @PrimaryKey(autoGenerate = true) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt deleted file mode 100644 index c3267f24..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import io.github.wulkanowy.services.sync.notifications.NotificationType -import io.github.wulkanowy.ui.modules.Destination -import java.time.Instant - -@Entity(tableName = "Notifications") -data class Notification( - - @ColumnInfo(name = "student_id") - val studentId: Long, - - val title: String, - - val content: String, - - val type: NotificationType, - - @ColumnInfo(defaultValue = "{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}") - val destination: Destination, - - val date: Instant, - - val data: String? = null -) { - @PrimaryKey(autoGenerate = true) - var id: Long = 0 -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt index d09742cd..60e67d32 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt @@ -1,22 +1,40 @@ package io.github.wulkanowy.data.db.entities +import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey +import com.squareup.moshi.JsonClass import java.io.Serializable -@kotlinx.serialization.Serializable +@JsonClass(generateAdapter = true) @Entity(tableName = "Recipients") data class Recipient( - val mailboxGlobalKey: String, - val studentMailboxGlobalKey: String, - val fullName: String, - val userName: String, - val schoolShortName: String, - val type: MailboxType, + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "real_id") + val realId: String, + + val name: String, + + @ColumnInfo(name = "real_name") + val realName: String, + + @ColumnInfo(name = "login_id") + val loginId: Int, + + @ColumnInfo(name = "unit_id") + val unitId: Int, + + val role: Int, + + val hash: String + ) : Serializable { @PrimaryKey(autoGenerate = true) var id: Long = 0 - override fun toString() = userName + override fun toString() = name } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt new file mode 100644 index 00000000..0570a2ff --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "ReportingUnits") +data class ReportingUnit( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "real_id") + val unitId: Int, + + @ColumnInfo(name = "short") + val shortName: String, + + @ColumnInfo(name = "sender_id") + val senderId: Int, + + @ColumnInfo(name = "sender_name") + val senderName: String, + + val roles: List + +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt index 25e27ef1..c8731bde 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt @@ -9,8 +9,8 @@ import java.time.LocalDate @Entity(tableName = "SchoolAnnouncements") data class SchoolAnnouncement( - @ColumnInfo(name = "user_login_id") - val userLoginId: Int, + @ColumnInfo(name = "student_id") + val studentId: Int, val date: LocalDate, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt index 187890c9..3b1f0add 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt @@ -7,12 +7,7 @@ import androidx.room.PrimaryKey import java.io.Serializable import java.time.LocalDate -@Entity( - tableName = "Semesters", indices = [Index( - value = ["student_id", "diary_id", "kindergarten_diary_id", "semester_id"], - unique = true - )] -) +@Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)]) data class Semester( @ColumnInfo(name = "student_id") @@ -21,9 +16,6 @@ data class Semester( @ColumnInfo(name = "diary_id") val diaryId: Int, - @ColumnInfo(name = "kindergarten_diary_id", defaultValue = "0") - val kindergartenDiaryId: Int, - @ColumnInfo(name = "diary_name") val diaryName: String, @@ -45,11 +37,12 @@ data class Semester( @ColumnInfo(name = "unit_id") val unitId: Int -) : Serializable { +): Serializable { @PrimaryKey(autoGenerate = true) var id: Long = 0 + @ColumnInfo(name = "is_current") var current: Boolean = false } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt index e1116733..af9fe831 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt @@ -5,7 +5,7 @@ import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey import java.io.Serializable -import java.time.Instant +import java.time.LocalDateTime @Entity( tableName = "Students", @@ -19,9 +19,6 @@ data class Student( @ColumnInfo(name = "scrapper_base_url") val scrapperBaseUrl: String, - @ColumnInfo(name = "scrapper_domain_suffix", defaultValue = "") - val scrapperDomainSuffix: String, - @ColumnInfo(name = "mobile_base_url") val mobileBaseUrl: String, @@ -77,7 +74,7 @@ data class Student( val isCurrent: Boolean, @ColumnInfo(name = "registration_date") - val registrationDate: Instant, + val registrationDate: LocalDateTime ) : Serializable { @PrimaryKey(autoGenerate = true) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentName.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentName.kt deleted file mode 100644 index 46f754b5..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentName.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import java.io.Serializable - -@Entity -data class StudentName( - - @ColumnInfo(name = "student_name") - val studentName: String - -) : Serializable { - - @PrimaryKey - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt index d23d388f..1bf159ef 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt @@ -4,8 +4,8 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import java.io.Serializable -import java.time.Instant import java.time.LocalDate +import java.time.LocalDateTime @Entity(tableName = "Timetable") data class Timetable( @@ -18,9 +18,9 @@ data class Timetable( val number: Int, - val start: Instant, + val start: LocalDateTime, - val end: Instant, + val end: LocalDateTime, val date: LocalDate, @@ -50,7 +50,4 @@ data class Timetable( @PrimaryKey(autoGenerate = true) var id: Long = 0 - - @ColumnInfo(name = "is_notified") - var isNotified: Boolean = true } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt index 47802610..c1f1365f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt @@ -4,9 +4,8 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import java.io.Serializable -import java.time.Instant import java.time.LocalDate -import java.util.* +import java.time.LocalDateTime @Entity(tableName = "TimetableAdditional") data class TimetableAdditional( @@ -17,9 +16,9 @@ data class TimetableAdditional( @ColumnInfo(name = "diary_id") val diaryId: Int, - val start: Instant, + val start: LocalDateTime, - val end: Instant, + val end: LocalDateTime, val date: LocalDate, @@ -28,10 +27,4 @@ data class TimetableAdditional( @PrimaryKey(autoGenerate = true) var id: Long = 0 - - @ColumnInfo(name = "repeat_id", defaultValue = "NULL") - var repeatId: UUID? = null - - @ColumnInfo(name = "is_added_by_user", defaultValue = "0") - var isAddedByUser: Boolean = false } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt index c827b82b..1dc38e14 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt @@ -43,14 +43,12 @@ class Migration12 : Migration(11, 12) { private fun getStudentsIds(database: SupportSQLiteDatabase): List { val students = mutableListOf() - database.query("SELECT student_id FROM Students").use { - if (it.moveToFirst()) { - do { - students.add(it.getInt(0)) - } while (it.moveToNext()) - } + val studentsCursor = database.query("SELECT student_id FROM Students") + if (studentsCursor.moveToFirst()) { + do { + students.add(studentsCursor.getInt(0)) + } while (studentsCursor.moveToNext()) } - return students } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt index 36de1e83..0cf8cd9b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt @@ -25,14 +25,12 @@ class Migration13 : Migration(12, 13) { private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList> { val students = mutableListOf>() - database.query("SELECT id, school_name FROM Students").use { - if (it.moveToFirst()) { - do { - students.add(it.getInt(0) to it.getString(1)) - } while (it.moveToNext()) - } + val studentsCursor = database.query("SELECT id, school_name FROM Students") + if (studentsCursor.moveToFirst()) { + do { + students.add(studentsCursor.getInt(0) to studentsCursor.getString(1)) + } while (studentsCursor.moveToNext()) } - return students } @@ -44,14 +42,12 @@ class Migration13 : Migration(12, 13) { private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List> { val students = mutableListOf>() - database.query("SELECT student_id, class_id FROM Students").use { - if (it.moveToFirst()) { - do { - students.add(it.getInt(0) to it.getInt(1)) - } while (it.moveToNext()) - } + val studentsCursor = database.query("SELECT student_id, class_id FROM Students") + if (studentsCursor.moveToFirst()) { + do { + students.add(studentsCursor.getInt(0) to studentsCursor.getInt(1)) + } while (studentsCursor.moveToNext()) } - return students } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt index 5c60beea..6592228a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt @@ -22,28 +22,24 @@ class Migration27 : Migration(26, 27) { private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList> { val students = mutableListOf>() - database.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))) - } while (it.moveToNext()) - } + val studentsCursor = database.query("SELECT id, user_login_id, student_name FROM Students") + if (studentsCursor.moveToFirst()) { + do { + students.add(Triple(studentsCursor.getLong(0), studentsCursor.getInt(1), studentsCursor.getString(2))) + } while (studentsCursor.moveToNext()) } - return students } private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList> { val units = mutableListOf>() - database.query("SELECT sender_id, sender_name FROM ReportingUnits").use { - if (it.moveToFirst()) { - do { - units.add(it.getInt(0) to it.getString(1)) - } while (it.moveToNext()) - } + val unitsCursor = database.query("SELECT sender_id, sender_name FROM ReportingUnits") + if (unitsCursor.moveToFirst()) { + do { + units.add(unitsCursor.getInt(0) to unitsCursor.getString(1)) + } while (unitsCursor.moveToNext()) } - return units } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt index f63431d0..cc540388 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt @@ -10,17 +10,15 @@ 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") - database.query("SELECT * FROM Students").use { - while (it.moveToNext()) { - val studentId = it.getLongOrNull(0) - database.execSQL( - """ - UPDATE Students - SET avatar_color = ${appInfo.defaultColorsForAvatar.random()} - WHERE id = $studentId - """ - ) - } + val studentsCursor = database.query("SELECT * FROM Students") + + while (studentsCursor.moveToNext()) { + val studentId = studentsCursor.getLongOrNull(0) + database.execSQL( + """UPDATE Students + SET avatar_color = ${appInfo.defaultColorsForAvatar.random()} + WHERE id = $studentId""" + ) } } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration40.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration40.kt deleted file mode 100644 index 6d2795c7..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration40.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration40 : Migration(39, 40) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL( - """ - CREATE TABLE IF NOT EXISTS `Notifications` ( - `student_id` INTEGER NOT NULL, - `title` TEXT NOT NULL, - `content` TEXT NOT NULL, - `type` TEXT NOT NULL, - `date` INTEGER NOT NULL, - `data` TEXT, - `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL - ) - """ - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration41.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration41.kt deleted file mode 100644 index ccaf8575..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration41.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase -import io.github.wulkanowy.data.db.SharedPrefProvider -import io.github.wulkanowy.data.enums.GradeExpandMode - -class Migration41(private val sharedPrefProvider: SharedPrefProvider) : Migration(40, 41) { - - override fun migrate(database: SupportSQLiteDatabase) { - migrateSharedPreferences() - database.execSQL("ALTER TABLE Homework ADD COLUMN is_added_by_user INTEGER NOT NULL DEFAULT 0") - } - - private fun migrateSharedPreferences() { - if (sharedPrefProvider.getBoolean("pref_key_expand_grade", false)) { - sharedPrefProvider.putString("pref_key_expand_grade_mode", GradeExpandMode.ALWAYS_EXPANDED.value) - } - sharedPrefProvider.delete("pref_key_expand_grade") - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration42.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration42.kt deleted file mode 100644 index 3d66f301..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration42.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration42 : Migration(41, 42) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL( - """CREATE TABLE IF NOT EXISTS `AdminMessages` ( - `id` INTEGER NOT NULL, - `title` TEXT NOT NULL, - `content` TEXT NOT NULL, - `version_name` INTEGER, - `version_max` INTEGER, - `target_register_host` TEXT, - `target_flavor` TEXT, - `destination_url` TEXT, - `priority` TEXT NOT NULL, - `type` TEXT NOT NULL, - PRIMARY KEY(`id`))""" - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration43.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration43.kt deleted file mode 100644 index 68c2834d..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration43.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -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") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration44.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration44.kt deleted file mode 100644 index 7bdcab5f..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration44.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -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") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt deleted file mode 100644 index d3fa5cf9..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt +++ /dev/null @@ -1,102 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase -import java.time.Instant -import java.time.ZoneId -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) - } - - private fun migrateConferences(database: SupportSQLiteDatabase) { - database.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") - } - } - } - - private fun migrateMessages(database: SupportSQLiteDatabase) { - database.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") - } - } - } - - private fun migrateMobileDevices(database: SupportSQLiteDatabase) { - database.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") - } - } - } - - private fun migrateNotifications(database: SupportSQLiteDatabase) { - database.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") - } - } - } - - private fun migrateTimetable(database: SupportSQLiteDatabase) { - database.query("SELECT * FROM Timetable").use { - while (it.moveToNext()) { - val id = it.getLong(it.getColumnIndexOrThrow("id")) - val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start")) - val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end")) - val timestampUtcStart = timestampLocalStart.timestampLocalToUTC() - val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC() - - database.execSQL("UPDATE Timetable SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id") - } - } - } - - private fun migrateTimetableAdditional(database: SupportSQLiteDatabase) { - database.query("SELECT * FROM TimetableAdditional").use { - while (it.moveToNext()) { - val id = it.getLong(it.getColumnIndexOrThrow("id")) - val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start")) - val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end")) - val timestampUtcStart = timestampLocalStart.timestampLocalToUTC() - val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC() - - database.execSQL("UPDATE TimetableAdditional SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id") - } - } - } - - private fun Long.timestampLocalToUTC(): Long = Instant.ofEpochMilli(this) - .atZone(ZoneOffset.UTC) - .withZoneSameLocal(ZoneId.of("Europe/Warsaw")) - .withZoneSameInstant(ZoneId.systemDefault()) - .toInstant() - .toEpochMilli() -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt deleted file mode 100644 index 6e1de19d..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration49 : Migration(48, 49) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS SchoolAnnouncements") - - database.execSQL( - """ - CREATE TABLE IF NOT EXISTS `SchoolAnnouncements` ( - `user_login_id` INTEGER NOT NULL, - `date` INTEGER NOT NULL, - `subject` TEXT NOT NULL, - `content` TEXT NOT NULL, - `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - `is_notified` INTEGER NOT NULL) - """.trimIndent() - ) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt deleted file mode 100644 index d45a8157..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration50 : Migration(49, 50) { - - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS MobileDevices") - database.execSQL( - """ - CREATE TABLE IF NOT EXISTS `MobileDevices` ( - `user_login_id` INTEGER NOT NULL, - `device_id` INTEGER NOT NULL, - `name` TEXT NOT NULL, - `date` INTEGER NOT NULL, - `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL) - """.trimIndent() - ) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt deleted file mode 100644 index e78e2e3a..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt +++ /dev/null @@ -1,88 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -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) - } - - private fun createMailboxTable(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS Mailboxes") - database.execSQL( - """ - CREATE TABLE IF NOT EXISTS `Mailboxes` ( - `globalKey` TEXT NOT NULL, - `fullName` TEXT NOT NULL, - `userName` TEXT NOT NULL, - `userLoginId` INTEGER NOT NULL, - `studentName` TEXT NOT NULL, - `schoolNameShort` TEXT NOT NULL, - `type` TEXT NOT NULL, - PRIMARY KEY(`globalKey`) - )""".trimIndent() - ) - } - - private fun recreateMessagesTable(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS Messages") - database.execSQL( - """ - CREATE TABLE IF NOT EXISTS `Messages` ( - `message_global_key` TEXT NOT NULL, - `mailbox_key` TEXT NOT NULL, - `message_id` INTEGER NOT NULL, - `correspondents` TEXT NOT NULL, - `subject` TEXT NOT NULL, - `date` INTEGER NOT NULL, - `folder_id` INTEGER NOT NULL, - `unread` INTEGER NOT NULL, - `has_attachments` INTEGER NOT NULL, - `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - `is_notified` INTEGER NOT NULL, - `content` TEXT NOT NULL, - `sender` TEXT, `recipients` TEXT - )""".trimIndent() - ) - } - - private fun recreateMessageAttachmentsTable(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS MessageAttachments") - database.execSQL( - """ - CREATE TABLE IF NOT EXISTS `MessageAttachments` ( - `real_id` INTEGER NOT NULL, - `message_global_key` TEXT NOT NULL, - `url` TEXT NOT NULL, - `filename` TEXT NOT NULL, - PRIMARY KEY(`real_id`) - )""".trimIndent() - ) - } - - private fun recreateRecipientsTable(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS Recipients") - database.execSQL( - """ - CREATE TABLE IF NOT EXISTS `Recipients` ( - `mailboxGlobalKey` TEXT NOT NULL, - `studentMailboxGlobalKey` TEXT NOT NULL, - `fullName` TEXT NOT NULL, - `userName` TEXT NOT NULL, - `schoolShortName` TEXT NOT NULL, - `type` TEXT NOT NULL, - `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL - )""".trimIndent() - ) - } - - private fun deleteReportingUnitTable(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS ReportingUnits") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt deleted file mode 100644 index 12624a51..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration53 : Migration(52, 53) { - - override fun migrate(database: SupportSQLiteDatabase) { - createMailboxTable(database) - recreateMessagesTable(database) - } - - private fun createMailboxTable(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS Mailboxes") - database.execSQL( - """ - CREATE TABLE IF NOT EXISTS `Mailboxes` ( - `globalKey` TEXT NOT NULL, - `email` TEXT NOT NULL, - `symbol` TEXT NOT NULL, - `schoolId` TEXT NOT NULL, - `fullName` TEXT NOT NULL, - `userName` TEXT NOT NULL, - `studentName` TEXT NOT NULL, - `schoolNameShort` TEXT NOT NULL, - `type` TEXT NOT NULL, - PRIMARY KEY(`globalKey`) - )""".trimIndent() - ) - } - - private fun recreateMessagesTable(database: SupportSQLiteDatabase) { - database.execSQL("DROP TABLE IF EXISTS Messages") - database.execSQL( - """ - CREATE TABLE IF NOT EXISTS `Messages` ( - `email` TEXT NOT NULL, - `message_global_key` TEXT NOT NULL, - `mailbox_key` TEXT NOT NULL, - `message_id` INTEGER NOT NULL, - `correspondents` TEXT NOT NULL, - `subject` TEXT NOT NULL, - `date` INTEGER NOT NULL, - `folder_id` INTEGER NOT NULL, - `unread` INTEGER NOT NULL, - `read_by` INTEGER, - `unread_by` INTEGER, - `has_attachments` INTEGER NOT NULL, - `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - `is_notified` INTEGER NOT NULL, - `content` TEXT NOT NULL, - `sender` TEXT, - `recipients` TEXT - )""".trimIndent() - ) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration54.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration54.kt deleted file mode 100644 index 678bd32f..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration54.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration54 : Migration(53, 54) { - - override fun migrate(database: SupportSQLiteDatabase) { - migrateResman(database) - removeTomaszowMazowieckiStudents(database) - } - - private fun migrateResman(database: SupportSQLiteDatabase) { - database.execSQL(""" - UPDATE Students SET - scrapper_base_url = 'https://vulcan.net.pl', - login_type = 'ADFSLightScoped', - symbol = 'rzeszowprojekt' - WHERE scrapper_base_url = 'https://resman.pl' - """.trimIndent()) - } - - private fun removeTomaszowMazowieckiStudents(database: SupportSQLiteDatabase) { - database.execSQL("DELETE FROM Students WHERE symbol = 'tomaszowmazowiecki'") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration55.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration55.kt deleted file mode 100644 index 424be171..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration55.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.DeleteColumn -import androidx.room.migration.AutoMigrationSpec -import androidx.sqlite.db.SupportSQLiteDatabase - -@DeleteColumn( - tableName = "MessageAttachments", - columnName = "real_id", -) -class Migration55 : AutoMigrationSpec { - - override fun onPostMigrate(db: SupportSQLiteDatabase) { - db.execSQL("DELETE FROM Messages") - db.execSQL("DELETE FROM MessageAttachments") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/AppTheme.kt b/app/src/main/java/io/github/wulkanowy/data/enums/AppTheme.kt deleted file mode 100644 index 438f0732..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/enums/AppTheme.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.wulkanowy.data.enums - -enum class AppTheme(val value: String) { - SYSTEM("system"), - LIGHT("light"), - DARK("dark"), - BLACK("black"); - - companion object { - fun getByValue(value: String) = values().find { it.value == value } ?: LIGHT - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/GradeColorTheme.kt b/app/src/main/java/io/github/wulkanowy/data/enums/GradeColorTheme.kt deleted file mode 100644 index 24b095d0..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/enums/GradeColorTheme.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.data.enums - -import java.io.Serializable - -enum class GradeColorTheme(val value: String) : Serializable { - VULCAN("vulcan"), - MATERIAL("material"), - GRADE_COLOR("grade_color"); - - companion object { - fun getByValue(value: String) = values().find { it.value == value } ?: VULCAN - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/GradeExpandMode.kt b/app/src/main/java/io/github/wulkanowy/data/enums/GradeExpandMode.kt deleted file mode 100644 index 96e4a174..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/enums/GradeExpandMode.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.data.enums - -enum class GradeExpandMode(val value: String) { - ONE("one"), - UNLIMITED("any"), - ALWAYS_EXPANDED("always"); - - companion object { - fun getByValue(value: String) = values().find { it.value == value } ?: ONE - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt b/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt deleted file mode 100644 index a7aa4cc2..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.data.enums - -enum class GradeSortingMode(val value: String) { - ALPHABETIC("alphabetic"), - DATE("date"), - AVERAGE("average"); - - companion object { - fun getByValue(value: String) = values().find { it.value == value } ?: ALPHABETIC - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/TimetableMode.kt b/app/src/main/java/io/github/wulkanowy/data/enums/TimetableMode.kt deleted file mode 100644 index 9e294ad7..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/enums/TimetableMode.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.data.enums - -enum class TimetableMode(val value: String) { - WHOLE_PLAN("yes"), - ONLY_CURRENT_GROUP("no"), - SMALL_OTHER_GROUP("small"); - - companion object { - fun getByValue(value: String) = values().find { it.value == value } ?: ONLY_CURRENT_GROUP - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt index c0ed0c8c..46e67fda 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt @@ -3,22 +3,17 @@ package io.github.wulkanowy.data.mappers import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance import io.github.wulkanowy.sdk.pojo.AttendanceSummary as SdkAttendanceSummary -fun List.mapToEntities(semester: Semester, lessons: List) = map { +fun List.mapToEntities(semester: Semester) = map { Attendance( studentId = semester.studentId, diaryId = semester.diaryId, date = it.date, timeId = it.timeId, number = it.number, - subject = it.subject.ifBlank { - lessons.find { lesson -> - lesson.date == it.date && lesson.number == it.number - }?.subject.orEmpty() - }, + subject = it.subject, name = it.name, presence = it.presence, absence = it.absence, diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt index add6439d..52dc9b30 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt @@ -10,9 +10,9 @@ fun List.mapToEntities(semester: Semester) = map { diaryId = semester.diaryId, agenda = it.agenda, conferenceId = it.id, - date = it.date.toInstant(), + date = it.date, presentOnConference = it.presentOnConference, - subject = it.topic, - title = it.place, + subject = it.subject, + title = it.title ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt index 16f1bbac..e6bf000b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt @@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformatio fun List.mapToEntities(student: Student) = map { SchoolAnnouncement( - userLoginId = student.userLoginId, + studentId = student.studentId, date = it.date, subject = it.subject, content = it.content, diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt index 173dfebf..bdb5efbb 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt @@ -11,7 +11,7 @@ fun List.mapToEntities(semester: Semester) = map { date = it.date, entryDate = it.entryDate, subject = it.subject, - group = "", + group = it.group, type = it.type, description = it.description, teacher = it.teacher, diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt deleted file mode 100644 index 0cf54777..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.data.mappers - -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.data.db.entities.MailboxType -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.pojo.Mailbox as SdkMailbox - -fun List.mapToEntities(student: Student) = map { - Mailbox( - globalKey = it.globalKey, - fullName = it.fullName, - userName = it.userName, - studentName = it.studentName, - schoolNameShort = it.schoolNameShort, - type = MailboxType.valueOf(it.type.name), - email = student.email, - symbol = student.symbol, - schoolId = student.schoolSymbol, - ) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt index a26d7665..913e4d03 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt @@ -1,45 +1,40 @@ package io.github.wulkanowy.data.mappers -import io.github.wulkanowy.data.db.entities.* -import io.github.wulkanowy.sdk.pojo.MailboxType -import timber.log.Timber +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageAttachment +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.Student +import java.time.LocalDateTime import io.github.wulkanowy.sdk.pojo.Message as SdkMessage import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient -fun List.mapToEntities( - student: Student, - mailbox: Mailbox?, - allMailboxes: List -): List = map { +fun List.mapToEntities(student: Student) = map { Message( - messageGlobalKey = it.globalKey, - mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box -> - box.fullName == it.mailbox - }?.globalKey.let { mailboxKey -> - if (mailboxKey == null) { - Timber.e("Can't find ${it.mailbox} in $allMailboxes") - "unknown" - } else mailboxKey - }, - email = student.email, - messageId = it.id, - correspondents = it.correspondents, + studentId = student.id, + realId = it.id ?: 0, + messageId = it.messageId ?: 0, + sender = it.sender?.name.orEmpty(), + senderId = it.sender?.loginId ?: 0, + recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów", subject = it.subject.trim(), - date = it.date.toInstant(), + date = it.date ?: LocalDateTime.now(), folderId = it.folderId, - unread = it.unread, - unreadBy = it.unreadBy, - readBy = it.readBy, - hasAttachments = it.hasAttachments, + unread = it.unread ?: false, + removed = it.removed, + hasAttachments = it.hasAttachments ).apply { content = it.content.orEmpty() + unreadBy = it.unreadBy ?: 0 + readBy = it.readBy ?: 0 } } -fun List.mapToEntities(messageGlobalKey: String) = map { +fun List.mapToEntities() = map { MessageAttachment( - messageGlobalKey = messageGlobalKey, + realId = it.id, + messageId = it.messageId, + oneDriveId = it.oneDriveId, url = it.url, filename = it.filename ) @@ -47,11 +42,12 @@ fun List.mapToEntities(messageGlobalKey: String) = map { fun List.mapFromEntities() = map { SdkRecipient( - fullName = it.fullName, - userName = it.userName, - studentName = it.userName, - mailboxGlobalKey = it.mailboxGlobalKey, - schoolNameShort = it.schoolShortName, - type = MailboxType.valueOf(it.type.name), + id = it.realId, + name = it.realName, + loginId = it.loginId, + reportingUnitId = it.unitId, + role = it.role, + hash = it.hash, + shortName = it.name ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt index 1f4178fa..f0c375bf 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt @@ -1,15 +1,15 @@ package io.github.wulkanowy.data.mappers import io.github.wulkanowy.data.db.entities.MobileDevice -import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.pojos.MobileDeviceToken -import io.github.wulkanowy.sdk.pojo.Device as SdkDevice import io.github.wulkanowy.sdk.pojo.Token as SdkToken +import io.github.wulkanowy.sdk.pojo.Device as SdkDevice -fun List.mapToEntities(student: Student) = map { +fun List.mapToEntities(semester: Semester) = map { MobileDevice( - userLoginId = student.userLoginId, - date = it.createDate.toInstant(), + userLoginId = semester.studentId, + date = it.createDate, deviceId = it.id, name = it.name ) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt index eb993a0f..80bddaab 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt @@ -1,16 +1,17 @@ package io.github.wulkanowy.data.mappers -import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient -fun List.mapToEntities(studentMailboxGlobalKey: String) = map { +fun List.mapToEntities(userLoginId: Int) = map { Recipient( - mailboxGlobalKey = it.mailboxGlobalKey, - fullName = it.fullName, - userName = it.userName, - studentMailboxGlobalKey = studentMailboxGlobalKey, - schoolShortName = it.schoolNameShort, - type = MailboxType.valueOf(it.type.name), + studentId = userLoginId, + realId = it.id, + realName = it.name, + name = it.shortName, + hash = it.hash, + loginId = it.loginId, + role = it.role, + unitId = it.reportingUnitId ?: 0 ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/RegisterUserMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/RegisterUserMapper.kt deleted file mode 100644 index 72c4861c..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/RegisterUserMapper.kt +++ /dev/null @@ -1,90 +0,0 @@ -package io.github.wulkanowy.data.mappers - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.data.pojos.* -import java.time.Instant -import io.github.wulkanowy.sdk.pojo.RegisterStudent as SdkRegisterStudent -import io.github.wulkanowy.sdk.pojo.RegisterUser as SdkRegisterUser - -fun SdkRegisterUser.mapToPojo(password: String?) = RegisterUser( - email = email, - login = login, - password = password, - scrapperBaseUrl = scrapperBaseUrl, - loginMode = loginMode, - loginType = loginType, - symbols = symbols.map { registerSymbol -> - RegisterSymbol( - symbol = registerSymbol.symbol, - error = registerSymbol.error, - hebeBaseUrl = registerSymbol.hebeBaseUrl, - keyId = registerSymbol.keyId, - privatePem = registerSymbol.privatePem, - userName = registerSymbol.userName, - schools = registerSymbol.schools.map { - RegisterUnit( - userLoginId = it.userLoginId, - schoolId = it.schoolId, - schoolName = it.schoolName, - schoolShortName = it.schoolShortName, - parentIds = it.parentIds, - studentIds = it.studentIds, - employeeIds = it.employeeIds, - error = it.error, - students = it.subjects - .filterIsInstance() - .map { registerSubject -> - RegisterStudent( - studentId = registerSubject.studentId, - studentName = registerSubject.studentName, - studentSecondName = registerSubject.studentSecondName, - studentSurname = registerSubject.studentSurname, - className = registerSubject.className, - classId = registerSubject.classId, - isParent = registerSubject.isParent, - semesters = registerSubject.semesters - .mapToEntities(registerSubject.studentId), - ) - }, - ) - } - ) - }, -) - -fun RegisterStudent.mapToStudentWithSemesters( - user: RegisterUser, - scrapperDomainSuffix: String, - symbol: RegisterSymbol, - unit: RegisterUnit, - colors: List, -): StudentWithSemesters = StudentWithSemesters( - semesters = semesters, - student = Student( - email = user.login, // for compatibility - userName = symbol.userName, - userLoginId = unit.userLoginId, - isParent = isParent, - className = className, - classId = classId, - studentId = studentId, - symbol = symbol.symbol, - loginType = user.loginType?.name.orEmpty(), - schoolName = unit.schoolName, - schoolShortName = unit.schoolShortName, - schoolSymbol = unit.schoolId, - studentName = "$studentName $studentSurname", - loginMode = user.loginMode.name, - scrapperBaseUrl = user.scrapperBaseUrl.orEmpty(), - scrapperDomainSuffix = scrapperDomainSuffix, - mobileBaseUrl = symbol.hebeBaseUrl.orEmpty(), - certificateKey = symbol.keyId.orEmpty(), - privateKey = symbol.privatePem.orEmpty(), - password = user.password.orEmpty(), - isCurrent = false, - registrationDate = Instant.now(), - ).apply { - avatarColor = colors.random() - }, -) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt new file mode 100644 index 00000000..6a21d59f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.pojo.ReportingUnit as SdkReportingUnit + +fun List.mapToEntities(student: Student) = map { + ReportingUnit( + studentId = student.id.toInt(), + unitId = it.id, + roles = it.roles, + senderId = it.senderId, + senderName = it.senderName, + shortName = it.short + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/SemesterMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/SemesterMapper.kt index 67d68a1e..acd93a91 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/SemesterMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/SemesterMapper.kt @@ -7,7 +7,6 @@ fun List.mapToEntities(studentId: Int) = map { Semester( studentId = studentId, diaryId = it.diaryId, - kindergartenDiaryId = it.kindergartenDiaryId, diaryName = it.diaryName, schoolYear = it.schoolYear, semesterId = it.semesterId, diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt new file mode 100644 index 00000000..c9332303 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import java.time.LocalDateTime +import io.github.wulkanowy.sdk.pojo.Student as SdkStudent + +fun List.mapToEntities(password: String = "", colors: List) = map { + StudentWithSemesters( + student = Student( + email = it.email, + password = password, + isParent = it.isParent, + symbol = it.symbol, + studentId = it.studentId, + userLoginId = it.userLoginId, + userName = it.userName, + studentName = it.studentName + " " + it.studentSurname, + schoolSymbol = it.schoolSymbol, + schoolShortName = it.schoolShortName, + schoolName = it.schoolName, + className = it.className, + classId = it.classId, + scrapperBaseUrl = it.scrapperBaseUrl, + loginType = it.loginType.name, + isCurrent = false, + registrationDate = LocalDateTime.now(), + mobileBaseUrl = it.mobileBaseUrl, + privateKey = it.privateKey, + certificateKey = it.certificateKey, + loginMode = it.loginMode.name, + ).apply { + avatarColor = colors.random() + }, + semesters = it.semesters.mapToEntities(it.studentId) + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt index ee525e10..045101c4 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt @@ -5,10 +5,10 @@ import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.TimetableAdditional import io.github.wulkanowy.data.db.entities.TimetableHeader import io.github.wulkanowy.data.pojos.TimetableFull -import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetableFull +import io.github.wulkanowy.sdk.pojo.TimetableFull as SdkTimetableFull import io.github.wulkanowy.sdk.pojo.TimetableDayHeader as SdkTimetableHeader -import io.github.wulkanowy.sdk.pojo.Lesson as SdkLesson -import io.github.wulkanowy.sdk.pojo.LessonAdditional as SdkTimetableAdditional +import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable +import io.github.wulkanowy.sdk.pojo.TimetableAdditional as SdkTimetableAdditional fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull( lessons = lessons.mapToEntities(semester), @@ -16,13 +16,13 @@ fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull( headers = headers.mapToEntities(semester) ) -fun List.mapToEntities(semester: Semester) = map { +fun List.mapToEntities(semester: Semester) = map { Timetable( studentId = semester.studentId, diaryId = semester.diaryId, number = it.number, - start = it.start.toInstant(), - end = it.end.toInstant(), + start = it.start, + end = it.end, date = it.date, subject = it.subject, subjectOld = it.subjectOld, @@ -45,8 +45,8 @@ fun List.mapToEntities(semester: Semester) = map { diaryId = semester.diaryId, subject = it.subject, date = it.date, - start = it.start.toInstant(), - end = it.end.toInstant(), + start = it.start, + end = it.end ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt index 4165b3f1..d2338c28 100644 --- a/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt @@ -1,8 +1,8 @@ package io.github.wulkanowy.data.pojos -import kotlinx.serialization.Serializable +import com.squareup.moshi.JsonClass -@Serializable +@JsonClass(generateAdapter = true) class Contributor( val displayName: String, val githubUsername: String diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/MessageDraft.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/MessageDraft.kt index 2e568e37..a79b70cd 100644 --- a/app/src/main/java/io/github/wulkanowy/data/pojos/MessageDraft.kt +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/MessageDraft.kt @@ -1,9 +1,9 @@ package io.github.wulkanowy.data.pojos +import com.squareup.moshi.JsonClass import io.github.wulkanowy.ui.modules.message.send.RecipientChipItem -import kotlinx.serialization.Serializable -@Serializable +@JsonClass(generateAdapter = true) data class MessageDraft( val recipients: List, val subject: String, diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/Notification.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/Notification.kt new file mode 100644 index 00000000..ca274937 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/Notification.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.data.pojos + +import androidx.annotation.DrawableRes +import androidx.annotation.PluralsRes +import androidx.annotation.StringRes +import io.github.wulkanowy.services.sync.notifications.NotificationType +import io.github.wulkanowy.ui.modules.main.MainView + +sealed interface Notification { + val type: NotificationType + val startMenu: MainView.Section + val icon: Int + val titleStringRes: Int + val contentStringRes: Int +} + +data class MultipleNotifications( + override val type: NotificationType, + override val startMenu: MainView.Section, + @DrawableRes override val icon: Int, + @PluralsRes override val titleStringRes: Int, + @PluralsRes override val contentStringRes: Int, + + @PluralsRes val summaryStringRes: Int, + val lines: List, +) : Notification + +data class OneNotification( + override val type: NotificationType, + override val startMenu: MainView.Section, + @DrawableRes override val icon: Int, + @StringRes override val titleStringRes: Int, + @StringRes override val contentStringRes: Int, + + val contentValues: List, +) : Notification diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/NotificationData.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/NotificationData.kt deleted file mode 100644 index f4fd0fc8..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/pojos/NotificationData.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.wulkanowy.data.pojos - -import io.github.wulkanowy.services.sync.notifications.NotificationType -import io.github.wulkanowy.ui.modules.Destination - -data class NotificationData( - val destination: Destination, - val title: String, - val content: String -) - -data class GroupNotificationData( - val notificationDataList: List, - val title: String, - val content: String, - val destination: Destination, - val type: NotificationType -) - diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/RegisterUser.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/RegisterUser.kt deleted file mode 100644 index 98bf1402..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/pojos/RegisterUser.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.data.pojos - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.sdk.scrapper.Scrapper - -data class RegisterUser( - val email: String, - val password: String?, - val login: String, // may be the same as email - val scrapperBaseUrl: String?, - val loginType: Scrapper.LoginType?, - val loginMode: Sdk.Mode, - val symbols: List, -) : java.io.Serializable - -data class RegisterSymbol( - val symbol: String, - val error: Throwable?, - val hebeBaseUrl: String?, - val keyId: String?, - val privatePem: String?, - val userName: String, - val schools: List, -) : java.io.Serializable - -data class RegisterUnit( - val userLoginId: Int, - val schoolId: String, - val schoolName: String, - val schoolShortName: String, - val parentIds: List, - val studentIds: List, - val employeeIds: List, - val error: Throwable?, - val students: List, -) : java.io.Serializable - -data class RegisterStudent( - val studentId: Int, - val studentName: String, - val studentSecondName: String, - val studentSurname: String, - val className: String, - val classId: Int, - val isParent: Boolean, - val semesters: List, -) : java.io.Serializable diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt deleted file mode 100644 index c9655b72..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.wulkanowy.data.repositories - -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.networkBoundResource -import io.github.wulkanowy.utils.AppInfo -import kotlinx.coroutines.sync.Mutex -import javax.inject.Inject -import javax.inject.Singleton - -@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 } - } - ) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt index bec2797d..71b7ea94 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt @@ -1,26 +1,25 @@ package io.github.wulkanowy.data.repositories -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext +import android.content.res.AssetManager +import com.squareup.moshi.Moshi +import com.squareup.moshi.Types import io.github.wulkanowy.data.pojos.Contributor import io.github.wulkanowy.utils.DispatchersProvider import kotlinx.coroutines.withContext -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.decodeFromStream import javax.inject.Inject import javax.inject.Singleton @Singleton class AppCreatorRepository @Inject constructor( - @ApplicationContext private val context: Context, - private val dispatchers: DispatchersProvider, - private val json: Json, + private val assets: AssetManager, + private val dispatchers: DispatchersProvider ) { - @OptIn(ExperimentalSerializationApi::class) - suspend fun getAppCreators() = withContext(dispatchers.io) { - val inputStream = context.assets.open("contributors.json").buffered() - json.decodeFromStream>(inputStream) + @Suppress("BlockingMethodInNonBlockingContext") + suspend fun getAppCreators() = withContext(dispatchers.backgroundThread) { + val moshi = Moshi.Builder().build() + val type = Types.newParameterizedType(List::class.java, Contributor::class.java) + val adapter = moshi.adapter>(type) + adapter.fromJson(assets.open("contributors.json").bufferedReader().use { it.readText() }) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt index 3afb9907..ffccb059 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt @@ -1,19 +1,20 @@ package io.github.wulkanowy.data.repositories import io.github.wulkanowy.data.db.dao.AttendanceDao -import io.github.wulkanowy.data.db.dao.TimetableDao import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Absent -import io.github.wulkanowy.utils.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.withContext import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime @@ -23,7 +24,6 @@ import javax.inject.Singleton @Singleton class AttendanceRepository @Inject constructor( private val attendanceDb: AttendanceDao, - private val timetableDb: TimetableDao, private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -32,72 +32,30 @@ class AttendanceRepository @Inject constructor( private val cacheKey = "attendance" - fun getAttendance( - student: Student, - semester: Semester, - start: LocalDate, - end: LocalDate, - forceRefresh: Boolean, - notify: Boolean = false, - ) = networkBoundResource( + fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( - key = getRefreshKey(cacheKey, semester, start, end) - ) - it.isEmpty() || forceRefresh || isExpired - }, - query = { - attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) - }, + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) }, + query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) }, fetch = { - val lessons = withContext(Dispatchers.IO) { - timetableDb.load( - semester.diaryId, semester.studentId, start.monday, end.sunday - ) - } - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getAttendance(start.monday, end.sunday) - .mapToEntities(semester, lessons) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getAttendance(start.monday, end.sunday, semester.semesterId) + .mapToEntities(semester) }, saveFetchResult = { old, new -> attendanceDb.deleteAll(old uniqueSubtract new) - val attendanceToAdd = (new uniqueSubtract old).map { newAttendance -> - newAttendance.apply { if (notify) isNotified = false } - } - attendanceDb.insertAll(attendanceToAdd) + attendanceDb.insertAll(new uniqueSubtract old) refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) }, filterResult = { it.filter { item -> item.date in start..end } } ) - fun getAttendanceFromDatabase( - semester: Semester, - start: LocalDate, - end: LocalDate - ): Flow> { - return attendanceDb.loadAll(semester.diaryId, semester.studentId, start, end) - } - - suspend fun updateTimetable(timetable: List) { - return attendanceDb.updateAll(timetable) - } - - suspend fun excuseForAbsence( - student: Student, semester: Semester, - absenceList: List, reason: String? = null - ) { - val items = absenceList.map { attendance -> + suspend fun excuseForAbsence(student: Student, semester: Semester, absenceList: List, reason: String? = null) { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance -> Absent( date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)), timeId = attendance.timeId ) - } - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .excuseForAbsence(items, reason) + }, reason) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt index 8e070913..58659914 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt @@ -4,11 +4,11 @@ import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import javax.inject.Inject @@ -29,18 +29,16 @@ class AttendanceSummaryRepository @Inject constructor( student: Student, semester: Semester, subjectId: Int, - forceRefresh: Boolean, + forceRefresh: Boolean ) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester)) - it.isEmpty() || forceRefresh || isExpired + it.isEmpty() || forceRefresh + || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) }, query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .getAttendanceSummary(subjectId) .mapToEntities(semester, subjectId) }, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt index 8f393cad..99ef56f4 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt @@ -4,9 +4,14 @@ import io.github.wulkanowy.data.db.dao.CompletedLessonsDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import java.time.LocalDate import javax.inject.Inject @@ -23,32 +28,12 @@ class CompletedLessonsRepository @Inject constructor( private val cacheKey = "completed" - fun getCompletedLessons( - student: Student, - semester: Semester, - start: LocalDate, - end: LocalDate, - forceRefresh: Boolean, - ) = networkBoundResource( + fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( - key = getRefreshKey(cacheKey, semester, start, end) - ) - it.isEmpty() || forceRefresh || isExpired - }, - query = { - completedLessonsDb.loadAll( - studentId = semester.studentId, - diaryId = semester.diaryId, - from = start.monday, - end = end.sunday - ) - }, + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) }, + query = { completedLessonsDb.loadAll(semester.studentId, semester.diaryId, start.monday, end.sunday) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .getCompletedLessons(start.monday, end.sunday) .mapToEntities(semester) }, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt index 83204cab..16d7c3c6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt @@ -5,15 +5,17 @@ import io.github.wulkanowy.data.db.entities.Conference import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.sync.Mutex import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneOffset import javax.inject.Inject import javax.inject.Singleton @@ -33,20 +35,18 @@ class ConferenceRepository @Inject constructor( semester: Semester, forceRefresh: Boolean, notify: Boolean = false, - startDate: Instant = Instant.EPOCH, + startDate: LocalDateTime = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC) ) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester)) - it.isEmpty() || forceRefresh || isExpired + it.isEmpty() || forceRefresh + || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) }, query = { conferenceDb.loadAll(semester.diaryId, student.studentId, startDate) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .getConferences() .mapToEntities(semester) .filter { it.date >= startDate } @@ -66,7 +66,7 @@ class ConferenceRepository @Inject constructor( conferenceDb.loadAll( diaryId = semester.diaryId, studentId = semester.studentId, - startDate = Instant.EPOCH, + startDate = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC) ) suspend fun updateConference(conference: List) = conferenceDb.updateAll(conference) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt index 013c0951..93d5a47c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt @@ -5,9 +5,14 @@ import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.endExamsDay +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.startExamsDay +import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.sync.Mutex import java.time.LocalDate @@ -31,15 +36,14 @@ class ExamRepository @Inject constructor( start: LocalDate, end: LocalDate, forceRefresh: Boolean, - notify: Boolean = false, + notify: Boolean = false ) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( + val isShouldBeRefreshed = refreshHelper.isShouldBeRefreshed( key = getRefreshKey(cacheKey, semester, start, end) ) - it.isEmpty() || forceRefresh || isExpired + it.isEmpty() || forceRefresh || isShouldBeRefreshed }, query = { examDb.loadAll( @@ -50,9 +54,8 @@ class ExamRepository @Inject constructor( ) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getExams(start.startExamsDay, start.endExamsDay) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getExams(start.startExamsDay, start.endExamsDay, semester.semesterId) .mapToEntities(semester) }, saveFetchResult = { old, new -> diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt index f5f895d8..0b8a0e71 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt @@ -7,14 +7,17 @@ import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.sync.Mutex -import java.time.Instant +import java.time.LocalDateTime import javax.inject.Inject import javax.inject.Singleton @@ -30,20 +33,11 @@ class GradeRepository @Inject constructor( private val cacheKey = "grade" - fun getGrades( - student: Student, - semester: Semester, - forceRefresh: Boolean, - notify: Boolean = false, - ) = networkBoundResource( + fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { - //When details is empty and summary is not, app will not use summary cache - edge case - it.first.isEmpty() - }, shouldFetch = { (details, summaries) -> - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester)) - details.isEmpty() || summaries.isEmpty() || forceRefresh || isExpired + val isShouldBeRefreshed = refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) + details.isEmpty() || summaries.isEmpty() || forceRefresh || isShouldBeRefreshed }, query = { val detailsFlow = gradeDb.loadAll(semester.semesterId, semester.studentId) @@ -52,7 +46,7 @@ class GradeRepository @Inject constructor( }, fetch = { val (details, summary) = sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + .switchDiary(semester.diaryId, semester.schoolYear) .getGrades(semester.semesterId) details.mapToEntities(semester) to summary.mapToEntities(semester) @@ -65,14 +59,8 @@ class GradeRepository @Inject constructor( } ) - private suspend fun refreshGradeDetails( - student: Student, - oldGrades: List, - newDetails: List, - notify: Boolean - ) { - val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date - ?: student.registrationDate.toLocalDate() + private suspend fun refreshGradeDetails(student: Student, oldGrades: List, newDetails: List, notify: Boolean) { + val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date ?: student.registrationDate.toLocalDate() gradeDb.deleteAll(oldGrades uniqueSubtract newDetails) gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach { if (it.date >= notifyBreakDate) it.apply { @@ -82,14 +70,10 @@ class GradeRepository @Inject constructor( }) } - private suspend fun refreshGradeSummaries( - oldSummaries: List, - newSummary: List, - notify: Boolean - ) { + private suspend fun refreshGradeSummaries(oldSummaries: List, newSummary: List, notify: Boolean) { gradeSummaryDb.deleteAll(oldSummaries uniqueSubtract newSummary) gradeSummaryDb.insertAll((newSummary uniqueSubtract oldSummaries).onEach { summary -> - val oldSummary = oldSummaries.find { old -> old.subject == summary.subject } + val oldSummary = oldSummaries.find { oldSummary -> oldSummary.subject == summary.subject } summary.isPredictedGradeNotified = when { summary.predictedGrade.isEmpty() -> true notify && oldSummary?.predictedGrade != summary.predictedGrade -> false @@ -102,13 +86,13 @@ class GradeRepository @Inject constructor( } summary.predictedGradeLastChange = when { - oldSummary == null -> Instant.now() - summary.predictedGrade != oldSummary.predictedGrade -> Instant.now() + oldSummary == null -> LocalDateTime.now() + summary.predictedGrade != oldSummary.predictedGrade -> LocalDateTime.now() else -> oldSummary.predictedGradeLastChange } summary.finalGradeLastChange = when { - oldSummary == null -> Instant.now() - summary.finalGrade != oldSummary.finalGrade -> Instant.now() + oldSummary == null -> LocalDateTime.now() + summary.finalGrade != oldSummary.finalGrade -> LocalDateTime.now() else -> oldSummary.finalGradeLastChange } }) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt index 9fa06c49..9cd8e711 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt @@ -11,14 +11,14 @@ import io.github.wulkanowy.data.mappers.mapPartialToStatisticItems import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex -import java.util.* +import java.util.Locale import javax.inject.Inject import javax.inject.Singleton @@ -39,24 +39,12 @@ class GradeStatisticsRepository @Inject constructor( private val semesterCacheKey = "grade_stats_semester" private val pointsCacheKey = "grade_stats_points" - fun getGradesPartialStatistics( - student: Student, - semester: Semester, - subjectName: String, - forceRefresh: Boolean, - ) = networkBoundResource( + fun getGradesPartialStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource( mutex = partialMutex, - isResultEmpty = { it.isEmpty() }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( - key = getRefreshKey(partialCacheKey, semester) - ) - it.isEmpty() || forceRefresh || isExpired - }, + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(partialCacheKey, semester)) }, query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .getGradesPartialStatistics(semester.semesterId) .mapToEntities(semester) }, @@ -68,40 +56,32 @@ class GradeStatisticsRepository @Inject constructor( mapResult = { items -> when (subjectName) { "Wszystkie" -> { - val summaryItem = GradePartialStatistics( + val numerator = items.map { + it.classAverage.replace(",", ".").toDoubleOrNull() ?: .0 + }.filterNot { it == .0 } + (items.reversed() + GradePartialStatistics( studentId = semester.studentId, semesterId = semester.semesterId, subject = subjectName, - classAverage = items.map { it.classAverage }.getSummaryAverage(), - studentAverage = items.map { it.studentAverage }.getSummaryAverage(), + classAverage = if (numerator.isEmpty()) "" else numerator.average().let { + "%.2f".format(Locale.FRANCE, it) + }, + studentAverage = "", classAmounts = items.map { it.classAmounts }.sumGradeAmounts(), studentAmounts = items.map { it.studentAmounts }.sumGradeAmounts() - ) - listOf(summaryItem) + items + )).reversed() } else -> items.filter { it.subject == subjectName } }.mapPartialToStatisticItems() } ) - fun getGradesSemesterStatistics( - student: Student, - semester: Semester, - subjectName: String, - forceRefresh: Boolean, - ) = networkBoundResource( + fun getGradesSemesterStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource( mutex = semesterMutex, - isResultEmpty = { it.isEmpty() }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( - key = getRefreshKey(semesterCacheKey, semester) - ) - it.isEmpty() || forceRefresh || isExpired - }, + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(semesterCacheKey, semester)) }, query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .getGradesSemesterStatistics(semester.semesterId) .mapToEntities(semester) }, @@ -114,50 +94,36 @@ class GradeStatisticsRepository @Inject constructor( val itemsWithAverage = items.map { item -> item.copy().apply { val denominator = item.amounts.sum() - classAverage = if (denominator == 0) "" else { - (item.amounts.mapIndexed { gradeValue, amount -> - (gradeValue + 1) * amount - }.sum().toDouble() / denominator).asAverageString() + average = if (denominator == 0) "" else (item.amounts.mapIndexed { gradeValue, amount -> + (gradeValue + 1) * amount + }.sum().toDouble() / denominator).let { + "%.2f".format(Locale.FRANCE, it) } } } when (subjectName) { - "Wszystkie" -> { - val summaryItem = GradeSemesterStatistics( - studentId = semester.studentId, - semesterId = semester.semesterId, - subject = subjectName, - amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(), - studentGrade = 0, - ).apply { - classAverage = itemsWithAverage.map { it.classAverage }.getSummaryAverage() - studentAverage = items - .mapNotNull { summary -> summary.studentGrade.takeIf { it != 0 } } - .average().asAverageString() + "Wszystkie" -> (itemsWithAverage.reversed() + GradeSemesterStatistics( + studentId = semester.studentId, + semesterId = semester.semesterId, + subject = subjectName, + amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(), + studentGrade = 0 + ).apply { + average = itemsWithAverage.mapNotNull { it.average.replace(",", ".").toDoubleOrNull() }.average().let { + "%.2f".format(Locale.FRANCE, it) } - listOf(summaryItem) + itemsWithAverage - } + }).reversed() else -> itemsWithAverage.filter { it.subject == subjectName } }.mapSemesterToStatisticItems() } ) - fun getGradesPointsStatistics( - student: Student, - semester: Semester, - subjectName: String, - forceRefresh: Boolean, - ) = networkBoundResource( + fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource( mutex = pointsMutex, - isResultEmpty = { it.isEmpty() }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(pointsCacheKey, semester)) - it.isEmpty() || forceRefresh || isExpired - }, + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(pointsCacheKey, semester)) }, query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .getGradesPointsStatistics(semester.semesterId) .mapToEntities(semester) }, @@ -174,19 +140,6 @@ class GradeStatisticsRepository @Inject constructor( } ) - private fun List.getSummaryAverage(): String { - val averages = mapNotNull { - it.replace(",", ".").toDoubleOrNull() - } - - return averages.average() - .asAverageString() - .takeIf { averages.isNotEmpty() } - .orEmpty() - } - - private fun Double.asAverageString(): String = "%.2f".format(Locale.FRANCE, this) - private fun List>.sumGradeAmounts(): List { val result = mutableListOf(0, 0, 0, 0, 0, 0) forEach { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt index f564824d..23dd74c2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt @@ -5,9 +5,14 @@ import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import java.time.LocalDate import javax.inject.Inject @@ -25,20 +30,16 @@ class HomeworkRepository @Inject constructor( private val cacheKey = "homework" fun getHomework( - student: Student, - semester: Semester, - start: LocalDate, - end: LocalDate, - forceRefresh: Boolean, - notify: Boolean = false, + student: Student, semester: Semester, + start: LocalDate, end: LocalDate, + forceRefresh: Boolean, notify: Boolean = false ) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( + val isShouldBeRefreshed = refreshHelper.isShouldBeRefreshed( key = getRefreshKey(cacheKey, semester, start, end) ) - it.isEmpty() || forceRefresh || isExpired + it.isEmpty() || forceRefresh || isShouldBeRefreshed }, query = { homeworkDb.loadAll( @@ -49,8 +50,7 @@ class HomeworkRepository @Inject constructor( ) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .getHomework(start.monday, end.sunday) .mapToEntities(semester) }, @@ -58,9 +58,8 @@ class HomeworkRepository @Inject constructor( val homeWorkToSave = (new uniqueSubtract old).onEach { if (notify) it.isNotified = false } - val filteredOld = old.filterNot { it.isAddedByUser } - homeworkDb.deleteAll(filteredOld uniqueSubtract new) + homeworkDb.deleteAll(old uniqueSubtract new) homeworkDb.insertAll(homeWorkToSave) refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) @@ -77,8 +76,4 @@ class HomeworkRepository @Inject constructor( homeworkDb.loadAll(semester.semesterId, semester.studentId, start.monday, end.sunday) suspend fun updateHomework(homework: List) = homeworkDb.updateAll(homework) - - suspend fun saveHomework(homework: Homework) = homeworkDb.insertAll(listOf(homework)) - - suspend fun deleteHomework(homework: Homework) = homeworkDb.deleteAll(listOf(homework)) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/LoggerRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/LoggerRepository.kt index 1a8cd6ea..6d509b02 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/LoggerRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/LoggerRepository.kt @@ -15,23 +15,24 @@ class LoggerRepository @Inject constructor( suspend fun getLastLogLines() = getLastModified().readText().split("\n") - suspend fun getLogFiles() = withContext(dispatchers.io) { - File(context.filesDir.absolutePath).listFiles(File::isFile) - ?.filter { it.name.endsWith(".log") }!! + suspend fun getLogFiles() = withContext(dispatchers.backgroundThread) { + File(context.filesDir.absolutePath).listFiles(File::isFile)?.filter { + it.name.endsWith(".log") + }!! } - private suspend fun getLastModified() = withContext(dispatchers.io) { - var lastModifiedTime = Long.MIN_VALUE - var chosenFile: File? = null - - File(context.filesDir.absolutePath).listFiles(File::isFile) - ?.forEach { file -> + private suspend fun getLastModified(): File { + return withContext(dispatchers.backgroundThread) { + var lastModifiedTime = Long.MIN_VALUE + var chosenFile: File? = null + File(context.filesDir.absolutePath).listFiles(File::isFile)?.forEach { file -> if (file.lastModified() > lastModifiedTime) { lastModifiedTime = file.lastModified() chosenFile = file } } - - chosenFile ?: throw FileNotFoundException("Log file not found") + if (chosenFile == null) throw FileNotFoundException("Log file not found") + chosenFile!! + } } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt index 87e8410f..b904b7db 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt @@ -4,9 +4,9 @@ import io.github.wulkanowy.data.db.dao.LuckyNumberDao import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntity -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.sync.Mutex @@ -23,18 +23,11 @@ class LuckyNumberRepository @Inject constructor( private val saveFetchResultMutex = Mutex() - fun getLuckyNumber( - student: Student, - forceRefresh: Boolean, - notify: Boolean = false, - ) = networkBoundResource( + fun getLuckyNumber(student: Student, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it == null }, shouldFetch = { it == null || forceRefresh }, query = { luckyNumberDb.load(student.studentId, now()) }, - fetch = { - sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student) - }, + fetch = { sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student) }, saveFetchResult = { old, new -> if (new != old) { old?.let { luckyNumberDb.deleteAll(listOfNotNull(it)) } @@ -48,11 +41,9 @@ class LuckyNumberRepository @Inject constructor( fun getLuckyNumberHistory(student: Student, start: LocalDate, end: LocalDate) = luckyNumberDb.getAll(student.studentId, start, end) - suspend fun getNotNotifiedLuckyNumber(student: Student) = - luckyNumberDb.load(student.studentId, now()).map { - if (it?.isNotified == false) it else null - }.first() + suspend fun getNotNotifiedLuckyNumber(student: Student) = luckyNumberDb.load(student.studentId, now()).map { + if (it?.isNotified == false) it else null + }.first() - suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) = - luckyNumberDb.updateAll(listOfNotNull(luckyNumber)) + suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) = luckyNumberDb.updateAll(listOfNotNull(luckyNumber)) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt index 53d9bead..9977e1d5 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt @@ -1,34 +1,36 @@ package io.github.wulkanowy.data.repositories import android.content.Context +import com.squareup.moshi.Moshi 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.Message +import io.github.wulkanowy.data.db.entities.MessageWithAttachment +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.Semester +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.pojos.MessageDraft -import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase +import io.github.wulkanowy.data.pojos.MessageDraftJsonAdapter import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Folder +import io.github.wulkanowy.sdk.pojo.SentMessage import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource 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 +import java.time.LocalDateTime.now import javax.inject.Inject import javax.inject.Singleton @@ -40,97 +42,92 @@ class MessageRepository @Inject constructor( @ApplicationContext private val context: Context, private val refreshHelper: AutoRefreshHelper, private val sharedPrefProvider: SharedPrefProvider, - private val json: Json, - private val mailboxDao: MailboxDao, - private val getMailboxByStudentUseCase: GetMailboxByStudentUseCase, + private val moshi: Moshi, ) { private val saveFetchResultMutex = Mutex() - private val messagesCacheKey = "message" - private val mailboxCacheKey = "mailboxes" + private val cacheKey = "message" + @Suppress("UNUSED_PARAMETER") fun getMessages( - student: Student, - mailbox: Mailbox?, - folder: MessageFolder, - forceRefresh: Boolean, - notify: Boolean = false, + student: Student, semester: Semester, + folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false ): Flow>> = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( - key = getRefreshKey(messagesCacheKey, mailbox, folder) + it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed( + getRefreshKey(cacheKey, student, folder) ) - it.isEmpty() || forceRefresh || isExpired - }, - query = { - if (mailbox == null) { - messagesDb.loadAll(folder.id, student.email) - } else messagesDb.loadAll(mailbox.globalKey, folder.id) }, + query = { messagesDb.loadAll(student.id.toInt(), folder.id) }, fetch = { - sdk.init(student).getMessages( - folder = Folder.valueOf(folder.name), - mailboxKey = mailbox?.globalKey, - ).mapToEntities(student, mailbox, mailboxDao.loadAll(student.email)) + sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now()) + .mapToEntities(student) }, saveFetchResult = { old, new -> messagesDb.deleteAll(old uniqueSubtract new) messagesDb.insertAll((new uniqueSubtract old).onEach { it.isNotified = !notify }) + messagesDb.updateAll(getMessagesWithReadByChange(old, new, !notify)) - refreshHelper.updateLastRefreshTimestamp( - getRefreshKey(messagesCacheKey, mailbox, folder) - ) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder)) } ) + private fun getMessagesWithReadByChange( + old: List, new: List, + setNotified: Boolean + ): List { + val oldMeta = old.map { Triple(it, it.readBy, it.unreadBy) } + val newMeta = new.map { Triple(it, it.readBy, it.unreadBy) } + + val updatedItems = newMeta uniqueSubtract oldMeta + + return updatedItems.map { + val oldItem = old.find { item -> item.messageId == it.first.messageId } + it.first.apply { + id = oldItem?.id ?: 0 + isNotified = oldItem?.isNotified ?: setNotified + content = oldItem?.content.orEmpty() + } + } + } + fun getMessage( - student: Student, - message: Message, - markAsRead: Boolean = false, + student: Student, message: Message, markAsRead: Boolean = false ): Flow> = networkBoundResource( - isResultEmpty = { it?.message?.content.isNullOrBlank() }, 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() - }, - query = { - messagesDb.loadMessageWithAttachment(message.messageGlobalKey) + checkNotNull(it, { "This message no longer exist!" }) + Timber.d("Message content in db empty: ${it.message.content.isEmpty()}") + it.message.unread || it.message.content.isEmpty() }, + query = { messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) }, fetch = { sdk.init(student).getMessageDetails( - messageKey = it!!.message.messageGlobalKey, - markAsRead = message.unread && markAsRead, - ) + messageId = it!!.message.messageId, + folderId = message.folderId, + read = markAsRead, + id = message.realId + ).let { details -> + details.content to details.attachments.mapToEntities() + } }, - saveFetchResult = { old, new -> - checkNotNull(old) { "Fetched message no longer exist!" } - messagesDb.updateAll( - listOf(old.message.apply { - id = message.id - unread = !markAsRead - sender = new.sender - recipients = new.recipients.singleOrNull() ?: "Wielu adresatów" - content = content.ifBlank { new.content } - }) - ) - messageAttachmentDao.insertAttachments( - items = new.attachments.mapToEntities(message.messageGlobalKey), - ) - + saveFetchResult = { old, (downloadedMessage, attachments) -> + checkNotNull(old, { "Fetched message no longer exist!" }) + messagesDb.updateAll(listOf(old.message.apply { + id = old.message.id + unread = !markAsRead + content = content.ifBlank { downloadedMessage } + })) + messageAttachmentDao.insertAttachments(attachments) Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read") } ) - fun getMessagesFromDatabase(student: Student, mailbox: Mailbox?): Flow> { - return if (mailbox == null) { - messagesDb.loadAll(RECEIVED.id, student.email) - } else messagesDb.loadAll(mailbox.globalKey, RECEIVED.id) + fun getMessagesFromDatabase(student: Student): Flow> { + return messagesDb.loadAll(student.id.toInt(), RECEIVED.id) } suspend fun updateMessages(messages: List) { @@ -138,92 +135,33 @@ class MessageRepository @Inject constructor( } suspend fun sendMessage( - student: Student, - subject: String, - content: String, - recipients: List, - mailboxId: String, - ) { - sdk.init(student).sendMessage( - subject = subject, - content = content, - recipients = recipients.mapFromEntities(), - mailboxId = mailboxId, - ) - } - - suspend fun deleteMessages(student: Student, mailbox: Mailbox?, messages: List) { - val firstMessage = messages.first() - sdk.init(student).deleteMessages( - messages = messages.map { it.messageGlobalKey }, - removeForever = firstMessage.folderId == TRASHED.id, - ) - - if (firstMessage.folderId != TRASHED.id) { - val deletedMessages = messages.map { - it.copy(folderId = TRASHED.id) - .apply { - id = it.id - content = it.content - sender = it.sender - recipients = it.recipients - } - } - - messagesDb.updateAll(deletedMessages) - } else messagesDb.deleteAll(messages) - - getMessages( - student = student, - mailbox = mailbox, - folder = TRASHED, - forceRefresh = true, - ).first() - } - - suspend fun deleteMessage(student: Student, mailbox: Mailbox?, message: Message) { - deleteMessages(student, mailbox, listOf(message)) - } - - suspend fun getMailboxes(student: Student, forceRefresh: Boolean) = networkBoundResource( - mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( - key = getRefreshKey(mailboxCacheKey, student), - ) - it.isEmpty() || isExpired || forceRefresh - }, - query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) }, - fetch = { - sdk.init(student).getMailboxes().mapToEntities(student) - }, - saveFetchResult = { old, new -> - mailboxDao.deleteAll(old uniqueSubtract new) - mailboxDao.insertAll(new uniqueSubtract old) - - refreshHelper.updateLastRefreshTimestamp(getRefreshKey(mailboxCacheKey, student)) - } + student: Student, subject: String, content: String, + recipients: List + ): SentMessage = sdk.init(student).sendMessage( + subject = subject, + content = content, + recipients = recipients.mapFromEntities() ) - suspend fun getMailboxByStudent(student: Student): Mailbox? { - val mailbox = getMailboxByStudentUseCase(student) + suspend fun deleteMessage(student: Student, message: Message) { + val isDeleted = sdk.init(student).deleteMessages( + messages = listOf(message.messageId), message.folderId + ) - return if (mailbox == null) { - getMailboxes(student, forceRefresh = true) - .onResourceError { throw it } - .onResourceSuccess { Timber.i("Found ${it.size} new mailboxes") } - .waitForResult() - - getMailboxByStudentUseCase(student) - } else mailbox + if (message.folderId != MessageFolder.TRASHED.id && isDeleted) { + val deletedMessage = message.copy(folderId = MessageFolder.TRASHED.id).apply { + id = message.id + content = message.content + } + messagesDb.updateAll(listOf(deletedMessage)) + } else messagesDb.deleteAll(listOf(message)) } var draftMessage: MessageDraft? - get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_draft)) - ?.let { json.decodeFromString(it) } + get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft)) + ?.let { MessageDraftJsonAdapter(moshi).fromJson(it) } set(value) = sharedPrefProvider.putString( - context.getString(R.string.pref_key_message_draft), - value?.let { json.encodeToString(it) } + context.getString(R.string.pref_key_message_send_draft), + value?.let { MessageDraftJsonAdapter(moshi).toJson(it) } ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt index 07c6959e..4b333bc6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt @@ -6,12 +6,12 @@ import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.data.pojos.MobileDeviceToken import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import javax.inject.Inject @@ -28,23 +28,14 @@ class MobileDeviceRepository @Inject constructor( private val cacheKey = "devices" - fun getDevices( - student: Student, - semester: Semester, - forceRefresh: Boolean, - ) = networkBoundResource( + fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) - it.isEmpty() || forceRefresh || isExpired - }, - query = { mobileDb.loadAll(student.userLoginId) }, + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student)) }, + query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .getRegisteredDevices() - .mapToEntities(student) + .mapToEntities(semester) }, saveFetchResult = { old, new -> mobileDb.deleteAll(old uniqueSubtract new) @@ -55,16 +46,14 @@ class MobileDeviceRepository @Inject constructor( ) suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .unregisterDevice(device.deviceId) mobileDb.deleteAll(listOf(device)) } suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken { - return sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .getToken() .mapToMobileDeviceToken() } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt index 4101803f..d43cdbc0 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt @@ -5,10 +5,14 @@ import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.sync.Mutex import javax.inject.Inject import javax.inject.Singleton @@ -24,25 +28,13 @@ class NoteRepository @Inject constructor( private val cacheKey = "note" - fun getNotes( - student: Student, - semester: Semester, - forceRefresh: Boolean, - notify: Boolean = false, - ) = networkBoundResource( + fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( - getRefreshKey(cacheKey, semester) - ) - it.isEmpty() || forceRefresh || isExpired - }, + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) }, query = { noteDb.loadAll(student.studentId) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getNotes() + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getNotes(semester.semesterId) .mapToEntities(semester) }, saveFetchResult = { old, new -> diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NotificationRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NotificationRepository.kt deleted file mode 100644 index fca26378..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/NotificationRepository.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.data.repositories - -import io.github.wulkanowy.data.db.dao.NotificationDao -import io.github.wulkanowy.data.db.entities.Notification -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NotificationRepository @Inject constructor( - private val notificationDao: NotificationDao, -) { - - fun getNotifications(studentId: Long) = notificationDao.loadAll(studentId) - - suspend fun saveNotification(notification: Notification) = - notificationDao.insertAll(listOf(notification)) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt index 348a4054..bc8100f2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt @@ -2,72 +2,68 @@ package io.github.wulkanowy.data.repositories import android.content.Context import android.content.SharedPreferences -import androidx.annotation.StringRes import androidx.core.content.edit import com.fredporciuncula.flow.preferences.FlowSharedPreferences import com.fredporciuncula.flow.preferences.Preference -import com.fredporciuncula.flow.preferences.Serializer +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import com.squareup.moshi.adapter import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R -import io.github.wulkanowy.data.enums.* +import io.github.wulkanowy.sdk.toLocalDate import io.github.wulkanowy.ui.modules.dashboard.DashboardItem import io.github.wulkanowy.ui.modules.grade.GradeAverageMode -import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem +import io.github.wulkanowy.ui.modules.grade.GradeSortingMode +import io.github.wulkanowy.utils.toTimestamp +import io.github.wulkanowy.utils.toLocalDateTime +import io.github.wulkanowy.utils.toTimestamp +import kotlinx.coroutines.ExperimentalCoroutinesApi 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 -import java.util.* +import java.time.LocalDate +import java.time.LocalDateTime import javax.inject.Inject import javax.inject.Singleton +@OptIn(ExperimentalCoroutinesApi::class) @Singleton class PreferencesRepository @Inject constructor( - @ApplicationContext val context: Context, private val sharedPref: SharedPreferences, private val flowSharedPref: FlowSharedPreferences, - private val json: Json, + @ApplicationContext val context: Context, + moshi: Moshi ) { + @OptIn(ExperimentalStdlibApi::class) + private val dashboardItemsPositionAdapter: JsonAdapter> = + moshi.adapter() + + val startMenuIndex: Int + get() = getString(R.string.pref_key_start_menu, R.string.pref_default_startup).toInt() + val isShowPresent: Boolean get() = getBoolean( R.string.pref_key_attendance_present, R.bool.pref_default_attendance_present ) - private val gradeAverageModePref: Preference - get() = getObjectFlow( - R.string.pref_key_grade_average_mode, - R.string.pref_default_grade_average_mode, - object : Serializer { - override fun serialize(value: GradeAverageMode) = value.value - override fun deserialize(serialized: String) = - GradeAverageMode.getByValue(serialized) - }, - ) - - val gradeAverageModeFlow: Flow - get() = gradeAverageModePref.asFlow() - - private val gradeAverageForceCalcPref: Preference - get() = flowSharedPref.getBoolean( - context.getString(R.string.pref_key_grade_average_force_calc), - context.resources.getBoolean(R.bool.pref_default_grade_average_force_calc) - ) - - val gradeAverageForceCalcFlow: Flow - get() = gradeAverageForceCalcPref.asFlow() - - val gradeExpandMode: GradeExpandMode - get() = GradeExpandMode.getByValue( + val gradeAverageMode: GradeAverageMode + get() = GradeAverageMode.getByValue( getString( - R.string.pref_key_expand_grade_mode, - R.string.pref_default_expand_grade_mode + R.string.pref_key_grade_average_mode, + R.string.pref_default_grade_average_mode ) ) + val gradeAverageForceCalc: Boolean + get() = getBoolean( + R.string.pref_key_grade_average_force_calc, + R.bool.pref_default_grade_average_force_calc + ) + + val isGradeExpandable: Boolean + get() = !getBoolean(R.string.pref_key_expand_grade, R.bool.pref_default_expand_grade) + val showAllSubjectsOnStatisticsList: Boolean get() = getBoolean( R.string.pref_key_grade_statistics_list, @@ -75,15 +71,13 @@ class PreferencesRepository @Inject constructor( ) val appThemeKey = context.getString(R.string.pref_key_app_theme) - val appTheme: AppTheme - get() = AppTheme.getByValue(getString(appThemeKey, R.string.pref_default_app_theme)) + val appTheme: String + get() = getString(appThemeKey, R.string.pref_default_app_theme) - val gradeColorTheme: GradeColorTheme - get() = GradeColorTheme.getByValue( - getString( - R.string.pref_key_grade_color_scheme, - R.string.pref_default_grade_color_scheme - ) + val gradeColorTheme: String + get() = getString( + R.string.pref_key_grade_color_scheme, + R.string.pref_default_grade_color_scheme ) val appLanguageKey = context.getString(R.string.pref_key_app_language) @@ -108,37 +102,12 @@ class PreferencesRepository @Inject constructor( val isUpcomingLessonsNotificationsEnableKey = context.getString(R.string.pref_key_notifications_upcoming_lessons_enable) - var isUpcomingLessonsNotificationsEnable: Boolean - set(value) { - sharedPref.edit { putBoolean(isUpcomingLessonsNotificationsEnableKey, value) } - } + val isUpcomingLessonsNotificationsEnable: Boolean get() = getBoolean( isUpcomingLessonsNotificationsEnableKey, R.bool.pref_default_notification_upcoming_lessons_enable ) - val isUpcomingLessonsNotificationsPersistentKey = - context.getString(R.string.pref_key_notifications_upcoming_lessons_persistent) - val isUpcomingLessonsNotificationsPersistent: Boolean - get() = getBoolean( - isUpcomingLessonsNotificationsPersistentKey, - R.bool.pref_default_notification_upcoming_lessons_persistent - ) - - val isNotificationPiggybackEnabledKey = - context.getString(R.string.pref_key_notifications_piggyback) - val isNotificationPiggybackEnabled: Boolean - get() = getBoolean( - R.string.pref_key_notifications_piggyback, - R.bool.pref_default_notification_piggyback - ) - - val isNotificationPiggybackRemoveOriginalEnabled: Boolean - get() = getBoolean( - R.string.pref_key_notifications_piggyback_cancel_original, - R.bool.pref_default_notification_piggyback_cancel_original - ) - val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug) val isDebugNotificationEnable: Boolean get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug) @@ -149,24 +118,12 @@ class PreferencesRepository @Inject constructor( R.string.pref_default_grade_modifier_plus ).toDouble() - val gradePlusModifierFlow: Flow - get() = getStringFlow( - R.string.pref_key_grade_modifier_plus, - R.string.pref_default_grade_modifier_plus - ).asFlow().map { it.toDouble() } - val gradeMinusModifier: Double get() = getString( R.string.pref_key_grade_modifier_minus, R.string.pref_default_grade_modifier_minus ).toDouble() - val gradeMinusModifierFlow: Flow - get() = getStringFlow( - R.string.pref_key_grade_modifier_minus, - R.string.pref_default_grade_modifier_minus - ).asFlow().map { it.toDouble() } - val fillMessageContent: Boolean get() = getBoolean( R.string.pref_key_fill_message_content, @@ -179,12 +136,10 @@ class PreferencesRepository @Inject constructor( R.bool.pref_default_timetable_show_groups ) - val showWholeClassPlan: TimetableMode - get() = TimetableMode.getByValue( - getString( - R.string.pref_key_timetable_show_whole_class, - R.string.pref_default_timetable_show_whole_class - ) + val showWholeClassPlan: String + get() = getString( + R.string.pref_key_timetable_show_whole_class, + R.string.pref_default_timetable_show_whole_class ) val gradeSortingMode: GradeSortingMode @@ -201,33 +156,42 @@ class PreferencesRepository @Inject constructor( R.bool.pref_default_timetable_show_timers ) + var isHomeworkFullscreen: Boolean + get() = getBoolean( + R.string.pref_key_homework_fullscreen, + R.bool.pref_default_homework_fullscreen + ) + set(value) = sharedPref.edit().putBoolean("homework_fullscreen", value).apply() + val showSubjectsWithoutGrades: Boolean get() = getBoolean( R.string.pref_key_subjects_without_grades, R.bool.pref_default_subjects_without_grades ) - val isOptionalArithmeticAverageFlow: Flow - get() = flowSharedPref.getBoolean( - context.getString(R.string.pref_key_optional_arithmetic_average), - context.resources.getBoolean(R.bool.pref_default_optional_arithmetic_average) - ).asFlow() + val isOptionalArithmeticAverage: Boolean + get() = getBoolean( + R.string.pref_key_optional_arithmetic_average, + R.bool.pref_default_optional_arithmetic_average + ) - var lasSyncDate: Instant? - get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date) - .takeIf { it != 0L }?.let(Instant::ofEpochMilli) - set(value) = sharedPref.edit().putLong("last_sync_date", value?.toEpochMilli() ?: 0).apply() + var lasSyncDate: LocalDateTime + get() = getLong( + R.string.pref_key_last_sync_date, + R.string.pref_default_last_sync_date + ).toLocalDateTime() + set(value) = sharedPref.edit().putLong("last_sync_date", value.toTimestamp()).apply() var dashboardItemsPosition: Map? get() { - val value = sharedPref.getString(PREF_KEY_DASHBOARD_ITEMS_POSITION, null) ?: return null + val json = sharedPref.getString(PREF_KEY_DASHBOARD_ITEMS_POSITION, null) ?: return null - return json.decodeFromString(value) + return dashboardItemsPositionAdapter.fromJson(json) } set(value) = sharedPref.edit { putString( PREF_KEY_DASHBOARD_ITEMS_POSITION, - json.encodeToString(value) + dashboardItemsPositionAdapter.toJson(value) ) } @@ -235,31 +199,17 @@ class PreferencesRepository @Inject constructor( get() = selectedDashboardTilesPreference.asFlow() .map { set -> set.map { DashboardItem.Tile.valueOf(it) } - .plus( - listOfNotNull( - DashboardItem.Tile.ACCOUNT, - DashboardItem.Tile.ADMIN_MESSAGE, - DashboardItem.Tile.ADS.takeIf { isAdsEnabled } - ) - ) + .plus(DashboardItem.Tile.ACCOUNT) .toSet() } var selectedDashboardTiles: Set get() = selectedDashboardTilesPreference.get() .map { DashboardItem.Tile.valueOf(it) } - .plus( - listOfNotNull( - DashboardItem.Tile.ACCOUNT, - DashboardItem.Tile.ADMIN_MESSAGE, - DashboardItem.Tile.ADS.takeIf { isAdsEnabled } - ) - ) + .plus(DashboardItem.Tile.ACCOUNT) .toSet() set(value) { - val filteredValue = value.filterNot { - it == DashboardItem.Tile.ACCOUNT || it == DashboardItem.Tile.ADMIN_MESSAGE - } + val filteredValue = value.filterNot { it == DashboardItem.Tile.ACCOUNT } .map { it.name } .toSet() @@ -275,104 +225,23 @@ class PreferencesRepository @Inject constructor( return flowSharedPref.getStringSet(prefKey, defaultSet) } - var dismissedAdminMessageIds: List - get() = sharedPref.getStringSet(PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS, emptySet()) - .orEmpty() - .map { it.toInt() } - set(value) = sharedPref.edit { - putStringSet(PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS, value.map { it.toString() }.toSet()) - } - var inAppReviewCount: Int get() = sharedPref.getInt(PREF_KEY_IN_APP_REVIEW_COUNT, 0) set(value) = sharedPref.edit().putInt(PREF_KEY_IN_APP_REVIEW_COUNT, value).apply() - var inAppReviewDate: Instant? - get() = sharedPref.getLong(PREF_KEY_IN_APP_REVIEW_DATE, 0).takeIf { it != 0L } - ?.let(Instant::ofEpochMilli) - set(value) = sharedPref.edit { - putLong(PREF_KEY_IN_APP_REVIEW_DATE, value?.toEpochMilli() ?: 0) - } + var inAppReviewDate: LocalDate? + get() = sharedPref.getLong(PREF_KEY_IN_APP_REVIEW_DATE, 0).takeIf { it != 0L }?.toLocalDate() + set(value) = sharedPref.edit().putLong(PREF_KEY_IN_APP_REVIEW_DATE, value!!.toTimestamp()).apply() var isAppReviewDone: Boolean get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false) - set(value) = sharedPref.edit { putBoolean(PREF_KEY_IN_APP_REVIEW_DONE, value) } - - var isAppSupportShown: Boolean - get() = sharedPref.getBoolean(PREF_KEY_APP_SUPPORT_SHOWN, false) - set(value) = sharedPref.edit { putBoolean(PREF_KEY_APP_SUPPORT_SHOWN, value) } - - var isAgreeToProcessData: Boolean - get() = getBoolean( - R.string.pref_key_ads_consent_data_processing, - R.bool.pref_default_ads_consent_data_processing - ) - set(value) = sharedPref.edit { - putBoolean(context.getString(R.string.pref_key_ads_consent_data_processing), value) - } - - var isPersonalizedAdsEnabled: Boolean - get() = sharedPref.getBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, false) - set(value) = sharedPref.edit { putBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, value) } - - val isAdsEnabledFlow = flowSharedPref.getBoolean( - context.getString(R.string.pref_key_ads_enabled), - context.resources.getBoolean(R.bool.pref_default_ads_enabled) - ).asFlow() - - var isAdsEnabled: Boolean - get() = getBoolean( - R.string.pref_key_ads_enabled, - R.bool.pref_default_ads_enabled - ) - set(value) = sharedPref.edit { - putBoolean(context.getString(R.string.pref_key_ads_enabled), value) - } - - var appMenuItemOrder: List - get() { - val value = sharedPref.getString(PREF_KEY_APP_MENU_ITEM_ORDER, null) - ?: return AppMenuItem.defaultAppMenuItemList - - return json.decodeFromString(value) - } - set(value) = sharedPref.edit { - putString( - PREF_KEY_APP_MENU_ITEM_ORDER, - json.encodeToString(value) - ) - } - - var installationId: String - get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty() - private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) } - - init { - if (installationId.isEmpty()) { - installationId = UUID.randomUUID().toString() - } - } + set(value) = sharedPref.edit().putBoolean(PREF_KEY_IN_APP_REVIEW_DONE, value).apply() private fun getLong(id: Int, default: Int) = getLong(context.getString(id), default) private fun getLong(id: String, default: Int) = sharedPref.getLong(id, context.resources.getString(default).toLong()) - private fun getStringFlow(id: Int, default: Int) = - flowSharedPref.getString(context.getString(id), context.getString(default)) - - private fun getObjectFlow( - @StringRes id: Int, - @StringRes default: Int, - serializer: Serializer - ): Preference = flowSharedPref.getObject( - key = context.getString(id), - serializer = serializer, - defaultValue = serializer.deserialize( - flowSharedPref.getString(context.getString(default)).get() - ) - ) - private fun getString(id: Int, default: Int) = getString(context.getString(id), default) private fun getString(id: String, default: Int) = @@ -384,14 +253,13 @@ class PreferencesRepository @Inject constructor( sharedPref.getBoolean(id, context.resources.getBoolean(default)) private companion object { - private const val PREF_KEY_APP_MENU_ITEM_ORDER = "app_menu_item_order" - private const val PREF_KEY_INSTALLATION_ID = "installation_id" + private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position" + private const val PREF_KEY_IN_APP_REVIEW_COUNT = "in_app_review_count" + private const val PREF_KEY_IN_APP_REVIEW_DATE = "in_app_review_date" + private const val PREF_KEY_IN_APP_REVIEW_DONE = "in_app_review_done" - private const val PREF_KEY_APP_SUPPORT_SHOWN = "app_support_shown" - private const val PREF_KEY_PERSONALIZED_ADS_ENABLED = "personalized_ads_enabled" - private const val PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS = "admin_message_dismissed_ids" } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt index 79984ce6..975a30a2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt @@ -1,11 +1,12 @@ package io.github.wulkanowy.data.repositories import io.github.wulkanowy.data.db.dao.RecipientDao -import io.github.wulkanowy.data.db.entities.* +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.AutoRefreshHelper -import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.uniqueSubtract import javax.inject.Inject @@ -14,50 +15,26 @@ import javax.inject.Singleton @Singleton class RecipientRepository @Inject constructor( private val recipientDb: RecipientDao, - private val sdk: Sdk, - private val refreshHelper: AutoRefreshHelper, + private val sdk: Sdk ) { - private val cacheKey = "recipient" - - suspend fun refreshRecipients(student: Student, mailbox: Mailbox, type: MailboxType) { - val new = sdk.init(student).getRecipients(mailbox.globalKey) - .mapToEntities(mailbox.globalKey) - val old = recipientDb.loadAll(type, mailbox.globalKey) + suspend fun refreshRecipients(student: Student, unit: ReportingUnit, role: Int) { + val new = sdk.init(student).getRecipients(unit.unitId, role).mapToEntities(unit.studentId) + val old = recipientDb.loadAll(unit.studentId, unit.unitId, role) recipientDb.deleteAll(old uniqueSubtract new) recipientDb.insertAll(new uniqueSubtract old) - - refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) } - suspend fun getRecipients( - student: Student, - mailbox: Mailbox?, - type: MailboxType, - ): List { - mailbox ?: return emptyList() + suspend fun getRecipients(student: Student, unit: ReportingUnit, role: Int): List { + return recipientDb.loadAll(unit.studentId, unit.unitId, role).ifEmpty { + refreshRecipients(student, unit, role) - val cached = recipientDb.loadAll(type, mailbox.globalKey) - - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) - return if (cached.isEmpty() || isExpired) { - refreshRecipients(student, mailbox, type) - recipientDb.loadAll(type, mailbox.globalKey) - } else cached + recipientDb.loadAll(unit.studentId, unit.unitId, role) + } } - suspend fun getMessageSender( - student: Student, - mailbox: Mailbox?, - message: Message, - ): List { - mailbox ?: return emptyList() - - return sdk.init(student) - .getMessageReplayDetails(message.messageGlobalKey) - .sender - .let(::listOf) - .mapToEntities(mailbox.globalKey) + suspend fun getMessageRecipients(student: Student, message: Message): List { + return sdk.init(student).getMessageRecipients(message.messageId, message.senderId).mapToEntities(student.studentId) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt index 5940f477..5e106355 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt @@ -11,7 +11,7 @@ class RecoverRepository @Inject constructor(private val sdk: Sdk) { return sdk.getPasswordResetCaptchaCode(host, symbol) } - suspend fun sendRecoverRequest( - url: String, symbol: String, email: String, reCaptchaResponse: String - ): String = sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse) + suspend fun sendRecoverRequest(url: String, symbol: String, email: String, reCaptchaResponse: String): String { + return sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse) + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt new file mode 100644 index 00000000..b9caf978 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.ReportingUnitDao +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ReportingUnitRepository @Inject constructor( + private val reportingUnitDb: ReportingUnitDao, + private val sdk: Sdk +) { + + suspend fun refreshReportingUnits(student: Student) { + val new = sdk.init(student).getReportingUnits().mapToEntities(student) + val old = reportingUnitDb.load(student.id.toInt()) + + reportingUnitDb.deleteAll(old.uniqueSubtract(new)) + reportingUnitDb.insertAll(new.uniqueSubtract(old)) + } + + suspend fun getReportingUnits(student: Student): List { + return reportingUnitDb.load(student.id.toInt()).ifEmpty { + refreshReportingUnits(student) + + reportingUnitDb.load(student.id.toInt()) + } + } + + suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? { + return reportingUnitDb.loadOne(student.id.toInt(), unitId) ?: run { + refreshReportingUnits(student) + + return reportingUnitDb.loadOne(student.id.toInt(), unitId) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt index 4c42d092..62d806ac 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt @@ -2,15 +2,17 @@ package io.github.wulkanowy.data.repositories import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.sync.Mutex import javax.inject.Inject import javax.inject.Singleton @@ -32,13 +34,13 @@ class SchoolAnnouncementRepository @Inject constructor( notify: Boolean = false ) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) - it.isEmpty() || forceRefresh || isExpired + it.isEmpty() || forceRefresh + || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student)) }, query = { - schoolAnnouncementDb.loadAll(student.userLoginId) + schoolAnnouncementDb.loadAll( + student.studentId) }, fetch = { sdk.init(student) @@ -55,11 +57,9 @@ class SchoolAnnouncementRepository @Inject constructor( refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) } ) - fun getSchoolAnnouncementFromDatabase(student: Student): Flow> { - return schoolAnnouncementDb.loadAll(student.userLoginId) + return schoolAnnouncementDb.loadAll(student.studentId) } - suspend fun updateSchoolAnnouncement(schoolAnnouncement: List) = - schoolAnnouncementDb.updateAll(schoolAnnouncement) + suspend fun updateSchoolAnnouncement(schoolAnnouncement: List) = schoolAnnouncementDb.updateAll(schoolAnnouncement) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt index 7972ed08..8b59cb58 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt @@ -4,11 +4,9 @@ import io.github.wulkanowy.data.db.dao.SchoolDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntity -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.AutoRefreshHelper -import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource import kotlinx.coroutines.sync.Mutex import javax.inject.Inject import javax.inject.Singleton @@ -16,44 +14,29 @@ import javax.inject.Singleton @Singleton class SchoolRepository @Inject constructor( private val schoolDb: SchoolDao, - private val sdk: Sdk, - private val refreshHelper: AutoRefreshHelper, + private val sdk: Sdk ) { private val saveFetchResultMutex = Mutex() - private val cacheKey = "school_info" - - fun getSchoolInfo( - student: Student, - semester: Semester, - forceRefresh: Boolean, - ) = networkBoundResource( - mutex = saveFetchResultMutex, - isResultEmpty = { it == null }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( - key = getRefreshKey(cacheKey, student) - ) - it == null || forceRefresh || isExpired - }, - query = { schoolDb.load(semester.studentId, semester.classId) }, - fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getSchool() - .mapToEntity(semester) - }, - saveFetchResult = { old, new -> - if (old != null && new != old) { - with(schoolDb) { - deleteAll(listOf(old)) - insertAll(listOf(new)) + fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) = + networkBoundResource( + mutex = saveFetchResultMutex, + shouldFetch = { it == null || forceRefresh }, + query = { schoolDb.load(semester.studentId, semester.classId) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool() + .mapToEntity(semester) + }, + saveFetchResult = { old, new -> + if (old != null && new != old) { + with(schoolDb) { + deleteAll(listOf(old)) + insertAll(listOf(new)) + } + } else if (old == null) { + schoolDb.insertAll(listOf(new)) } - } else if (old == null) { - schoolDb.insertAll(listOf(new)) } - refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) - } - ) + ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt index 92bb3708..8942391c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt @@ -5,7 +5,11 @@ import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.DispatchersProvider +import io.github.wulkanowy.utils.getCurrentOrLast +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.isCurrent +import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.withContext import timber.log.Timber import javax.inject.Inject @@ -18,11 +22,7 @@ class SemesterRepository @Inject constructor( private val dispatchers: DispatchersProvider ) { - suspend fun getSemesters( - student: Student, - forceRefresh: Boolean = false, - refreshOnNoCurrent: Boolean = false - ) = withContext(dispatchers.io) { + suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false) = withContext(dispatchers.backgroundThread) { val semesters = semesterDb.loadAll(student.studentId, student.classId) if (isShouldFetch(student, semesters, forceRefresh, refreshOnNoCurrent)) { @@ -31,25 +31,14 @@ class SemesterRepository @Inject constructor( } else semesters } - private fun isShouldFetch( - student: Student, - semesters: List, - forceRefresh: Boolean, - refreshOnNoCurrent: Boolean - ): Boolean { + private fun isShouldFetch(student: Student, semesters: List, forceRefresh: Boolean, refreshOnNoCurrent: Boolean): Boolean { val isNoSemesters = semesters.isEmpty() - val isRefreshOnModeChangeRequired = when { - Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE -> { - semesters.firstOrNull { it.isCurrent }?.let { - 0 == it.diaryId && 0 == it.kindergartenDiaryId - } == true - } - else -> false - } + val isRefreshOnModeChangeRequired = if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + semesters.firstOrNull { it.isCurrent }?.diaryId == 0 + } else false - val isRefreshOnNoCurrentAppropriate = - refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent } + val isRefreshOnNoCurrentAppropriate = refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent } return forceRefresh || isNoSemesters || isRefreshOnModeChangeRequired || isRefreshOnNoCurrentAppropriate } @@ -63,8 +52,7 @@ class SemesterRepository @Inject constructor( semesterDb.insertSemesters(new.uniqueSubtract(old)) } - suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = - withContext(dispatchers.io) { - getSemesters(student, forceRefresh).getCurrentOrLast() - } + suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = withContext(dispatchers.backgroundThread) { + getSemesters(student, forceRefresh).getCurrentOrLast() + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt index efc82a77..de66ad20 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt @@ -4,9 +4,9 @@ import io.github.wulkanowy.data.db.dao.StudentInfoDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntity -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource import kotlinx.coroutines.sync.Mutex import javax.inject.Inject import javax.inject.Singleton @@ -19,29 +19,24 @@ class StudentInfoRepository @Inject constructor( private val saveFetchResultMutex = Mutex() - fun getStudentInfo( - student: Student, - semester: Semester, - forceRefresh: Boolean, - ) = networkBoundResource( - mutex = saveFetchResultMutex, - isResultEmpty = { it == null }, - shouldFetch = { it == null || forceRefresh }, - query = { studentInfoDao.loadStudentInfo(student.studentId) }, - fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getStudentInfo().mapToEntity(semester) - }, - saveFetchResult = { old, new -> - if (old != null && new != old) { - with(studentInfoDao) { - deleteAll(listOf(old)) - insertAll(listOf(new)) + fun getStudentInfo(student: Student, semester: Semester, forceRefresh: Boolean) = + networkBoundResource( + mutex = saveFetchResultMutex, + shouldFetch = { it == null || forceRefresh }, + query = { studentInfoDao.loadStudentInfo(student.studentId) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getStudentInfo().mapToEntity(semester) + }, + saveFetchResult = { old, new -> + if (old != null && new != old) { + with(studentInfoDao) { + deleteAll(listOf(old)) + insertAll(listOf(new)) + } + } else if (old == null) { + studentInfoDao.insertAll(listOf(new)) } - } else if (old == null) { - studentInfoDao.insertAll(listOf(new)) } - } - ) + ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt index 42d1eb84..c2f364b3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt @@ -1,22 +1,17 @@ package io.github.wulkanowy.data.repositories import android.content.Context -import androidx.room.withTransaction import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.dao.SemesterDao import io.github.wulkanowy.data.db.dao.StudentDao -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 import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.exceptions.NoCurrentStudentException -import io.github.wulkanowy.data.mappers.mapToPojo -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.DispatchersProvider -import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.security.decrypt import io.github.wulkanowy.utils.security.encrypt import kotlinx.coroutines.withContext @@ -30,64 +25,56 @@ class StudentRepository @Inject constructor( private val studentDb: StudentDao, private val semesterDb: SemesterDao, private val sdk: Sdk, - private val appDatabase: AppDatabase + private val appInfo: AppInfo ) { + suspend fun isStudentSaved() = getSavedStudents(false).isNotEmpty() + suspend fun isCurrentStudentSet() = studentDb.loadCurrent()?.isCurrent ?: false suspend fun getStudentsApi( pin: String, symbol: String, token: String - ): RegisterUser = sdk - .getStudentsFromHebe(token, pin, symbol, "") - .mapToPojo(null) + ): List = + sdk.getStudentsFromMobileApi(token, pin, symbol, "") + .mapToEntities(colors = appInfo.defaultColorsForAvatar) - suspend fun getUserSubjectsFromScrapper( + suspend fun getStudentsScrapper( email: String, password: String, scrapperBaseUrl: String, - domainSuffix: String, symbol: String - ): RegisterUser = sdk - .getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, domainSuffix, symbol) - .mapToPojo(password) + ): List = + sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol) + .mapToEntities(password, appInfo.defaultColorsForAvatar) suspend fun getStudentsHybrid( email: String, password: String, scrapperBaseUrl: String, symbol: String - ): RegisterUser = sdk - .getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol) - .mapToPojo(password) + ): List = + sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol) + .mapToEntities(password, appInfo.defaultColorsForAvatar) suspend fun getSavedStudents(decryptPass: Boolean = true) = studentDb.loadStudentsWithSemesters() .map { it.apply { - if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) { - student.password = withContext(dispatchers.io) { + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + student.password = withContext(dispatchers.backgroundThread) { decrypt(student.password) } } } } - suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true) = - studentDb.loadStudentWithSemestersById(id)?.apply { - if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) { - student.password = withContext(dispatchers.io) { - decrypt(student.password) - } - } - } - suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student { val student = studentDb.loadById(id) ?: throw NoCurrentStudentException() - if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) { - student.password = withContext(dispatchers.io) { + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + student.password = withContext(dispatchers.backgroundThread) { decrypt(student.password) } } @@ -97,65 +84,40 @@ class StudentRepository @Inject constructor( suspend fun getCurrentStudent(decryptPass: Boolean = true): Student { val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException() - if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) { - student.password = withContext(dispatchers.io) { + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + student.password = withContext(dispatchers.backgroundThread) { decrypt(student.password) } } return student } - suspend fun saveStudents(studentsWithSemesters: List) { + suspend fun saveStudents(studentsWithSemesters: List): List { val semesters = studentsWithSemesters.flatMap { it.semesters } val students = studentsWithSemesters.map { it.student } .map { it.apply { - if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.HEBE) { - password = withContext(dispatchers.io) { + if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) { + password = withContext(dispatchers.backgroundThread) { encrypt(password, context) } } } } - .mapIndexed { index, student -> - if (index == 0) { - student.copy(isCurrent = true).apply { avatarColor = student.avatarColor } - } else student - } - appDatabase.withTransaction { - studentDb.resetCurrent() - semesterDb.insertSemesters(semesters) - studentDb.insertAll(students) - } + semesterDb.insertSemesters(semesters) + return studentDb.insertAll(students) } suspend fun switchStudent(studentWithSemesters: StudentWithSemesters) { - studentDb.switchCurrent(studentWithSemesters.student.id) + with(studentDb) { + resetCurrent() + updateCurrent(studentWithSemesters.student.id) + } } suspend fun logoutStudent(student: Student) = studentDb.delete(student) suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) = studentDb.update(studentNickAndAvatar) - - suspend fun isOneUniqueStudent() = getSavedStudents(false) - .distinctBy { it.student.studentName }.size == 1 - - suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) = - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .authorizePermission(pesel) - - suspend fun refreshStudentName(student: Student, semester: Semester) { - val newCurrentApiStudent = sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getCurrentStudent() ?: return - - val studentName = StudentName( - studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}" - ).apply { id = student.id } - - studentDb.update(studentName) - } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt index 3926122b..b4bfef18 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt @@ -4,11 +4,9 @@ import io.github.wulkanowy.data.db.dao.SubjectDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.AutoRefreshHelper -import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import javax.inject.Inject @@ -17,36 +15,22 @@ import javax.inject.Singleton @Singleton class SubjectRepository @Inject constructor( private val subjectDao: SubjectDao, - private val sdk: Sdk, - private val refreshHelper: AutoRefreshHelper, + private val sdk: Sdk ) { private val saveFetchResultMutex = Mutex() - private val cacheKey = "subjects" - - fun getSubjects( - student: Student, - semester: Semester, - forceRefresh: Boolean = false, - ) = networkBoundResource( + fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester)) - it.isEmpty() || forceRefresh || isExpired - }, + shouldFetch = { it.isEmpty() || forceRefresh }, query = { subjectDao.loadAll(semester.diaryId, semester.studentId) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .getSubjects().mapToEntities(semester) }, saveFetchResult = { old, new -> subjectDao.deleteAll(old uniqueSubtract new) subjectDao.insertAll(new uniqueSubtract old) - - refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) } ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt index 4e3b40f9..7135edbe 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt @@ -4,11 +4,9 @@ import io.github.wulkanowy.data.db.dao.TeacherDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.AutoRefreshHelper -import io.github.wulkanowy.utils.getRefreshKey import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import javax.inject.Inject @@ -17,37 +15,23 @@ import javax.inject.Singleton @Singleton class TeacherRepository @Inject constructor( private val teacherDb: TeacherDao, - private val sdk: Sdk, - private val refreshHelper: AutoRefreshHelper, + private val sdk: Sdk ) { private val saveFetchResultMutex = Mutex() - private val cacheKey = "teachers" - - fun getTeachers( - student: Student, - semester: Semester, - forceRefresh: Boolean, - ) = networkBoundResource( + fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester)) - it.isEmpty() || forceRefresh || isExpired - }, + shouldFetch = { it.isEmpty() || forceRefresh }, query = { teacherDb.loadAll(semester.studentId, semester.classId) }, fetch = { - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getTeachers() + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getTeachers(semester.semesterId) .mapToEntities(semester) }, saveFetchResult = { old, new -> teacherDb.deleteAll(old uniqueSubtract new) teacherDb.insertAll(new uniqueSubtract old) - - refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) } ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt index 136fb8d5..5495d077 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt @@ -3,17 +3,25 @@ package io.github.wulkanowy.data.repositories import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao import io.github.wulkanowy.data.db.dao.TimetableDao import io.github.wulkanowy.data.db.dao.TimetableHeaderDao -import io.github.wulkanowy.data.db.entities.* +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.db.entities.TimetableAdditional +import io.github.wulkanowy.data.db.entities.TimetableHeader import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.data.pojos.TimetableFull import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.sync.Mutex -import java.time.Instant import java.time.LocalDate import javax.inject.Inject import javax.inject.Singleton @@ -32,46 +40,30 @@ class TimetableRepository @Inject constructor( private val cacheKey = "timetable" - enum class TimetableType { - NORMAL, ADDITIONAL - } - fun getTimetable( - student: Student, - semester: Semester, - start: LocalDate, - end: LocalDate, - forceRefresh: Boolean, - refreshAdditional: Boolean = false, - notify: Boolean = false, - timetableType: TimetableType = TimetableType.NORMAL + student: Student, semester: Semester, start: LocalDate, end: LocalDate, + forceRefresh: Boolean, refreshAdditional: Boolean = false ) = networkBoundResource( mutex = saveFetchResultMutex, - isResultEmpty = { - when (timetableType) { - TimetableType.NORMAL -> it.lessons.isEmpty() - TimetableType.ADDITIONAL -> it.additional.isEmpty() - } - }, shouldFetch = { (timetable, additional, headers) -> val refreshKey = getRefreshKey(cacheKey, semester, start, end) - val isExpired = refreshHelper.shouldBeRefreshed(refreshKey) + val isShouldRefresh = refreshHelper.isShouldBeRefreshed(refreshKey) val isRefreshAdditional = additional.isEmpty() && refreshAdditional val isNoData = timetable.isEmpty() || isRefreshAdditional || headers.isEmpty() - isNoData || forceRefresh || isExpired + isNoData || forceRefresh || isShouldRefresh }, query = { getFullTimetableFromDatabase(student, semester, start, end) }, fetch = { val timetableFull = sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getTimetable(start.monday, end.sunday) + .switchDiary(semester.diaryId, semester.schoolYear) + .getTimetableFull(start.monday, end.sunday) timetableFull.mapToEntities(semester) }, saveFetchResult = { timetableOld, timetableNew -> - refreshTimetable(student, timetableOld.lessons, timetableNew.lessons, notify) + refreshTimetable(student, timetableOld.lessons, timetableNew.lessons) refreshAdditional(timetableOld.additional, timetableNew.additional) refreshDayHeaders(timetableOld.headers, timetableNew.headers) @@ -87,10 +79,8 @@ class TimetableRepository @Inject constructor( ) private fun getFullTimetableFromDatabase( - student: Student, - semester: Semester, - start: LocalDate, - end: LocalDate, + student: Student, semester: Semester, + start: LocalDate, end: LocalDate ): Flow { val timetableFlow = timetableDb.loadAll( diaryId = semester.diaryId, @@ -121,27 +111,21 @@ class TimetableRepository @Inject constructor( } } - fun getTimetableFromDatabase( - semester: Semester, - from: LocalDate, - end: LocalDate - ): Flow> { - return timetableDb.loadAll(semester.diaryId, semester.studentId, from, end) - } - - suspend fun updateTimetable(timetable: List) { - return timetableDb.updateAll(timetable) - } - private suspend fun refreshTimetable( student: Student, - lessonsOld: List, - lessonsNew: List, - notify: Boolean + lessonsOld: List, lessonsNew: List ) { val lessonsToRemove = lessonsOld uniqueSubtract lessonsNew val lessonsToAdd = (lessonsNew uniqueSubtract lessonsOld).map { new -> - new.apply { if (notify) isNotified = false } + val matchingOld = lessonsOld.singleOrNull { new.start == it.start } + if (matchingOld != null) { + val useOldTeacher = new.teacher.isEmpty() && !new.changes && !matchingOld.changes + new.copy( + room = if (new.room.isEmpty()) matchingOld.room else new.room, + teacher = if (useOldTeacher) matchingOld.teacher + else new.teacher + ) + } else new } timetableDb.deleteAll(lessonsToRemove) @@ -155,8 +139,7 @@ class TimetableRepository @Inject constructor( old: List, new: List ) { - val oldFiltered = old.filter { !it.isAddedByUser } - timetableAdditionalDb.deleteAll(oldFiltered uniqueSubtract new) + timetableAdditionalDb.deleteAll(old uniqueSubtract new) timetableAdditionalDb.insertAll(new uniqueSubtract old) } @@ -164,19 +147,4 @@ class TimetableRepository @Inject constructor( timetableHeaderDb.deleteAll(old uniqueSubtract new) timetableHeaderDb.insertAll(new uniqueSubtract old) } - - fun getLastRefreshTimestamp(semester: Semester, start: LocalDate, end: LocalDate): Instant { - val refreshKey = getRefreshKey(cacheKey, semester, start, end) - return refreshHelper.getLastRefreshTimestamp(refreshKey) - } - - suspend fun saveAdditionalList(additionalList: List) = - timetableAdditionalDb.insertAll(additionalList) - - suspend fun deleteAdditional(additional: TimetableAdditional, deleteSeries: Boolean) = - if (deleteSeries) { - timetableAdditionalDb.deleteAllByRepeatId(additional.repeatId!!) - } else { - timetableAdditionalDb.deleteAll(listOf(additional)) - } } diff --git a/app/src/main/java/io/github/wulkanowy/data/serializers/LocalDateSerializer.kt b/app/src/main/java/io/github/wulkanowy/data/serializers/LocalDateSerializer.kt deleted file mode 100644 index ba97d37a..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/serializers/LocalDateSerializer.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.data.serializers - -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.KSerializer -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.nullable -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder -import java.time.LocalDate - -@OptIn(ExperimentalSerializationApi::class) -object LocalDateSerializer : KSerializer { - - override val descriptor = PrimitiveSerialDescriptor("LocalDate", PrimitiveKind.LONG).nullable - - override fun serialize(encoder: Encoder, value: LocalDate?) { - if (value == null) { - encoder.encodeNull() - } else { - encoder.encodeNotNullMark() - encoder.encodeLong(value.toEpochDay()) - } - } - - override fun deserialize(decoder: Decoder): LocalDate? = - if (decoder.decodeNotNullMark()) { - LocalDate.ofEpochDay(decoder.decodeLong()) - } else { - decoder.decodeNull() - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt deleted file mode 100644 index 669514aa..00000000 --- a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt +++ /dev/null @@ -1,65 +0,0 @@ -package io.github.wulkanowy.domain.messages - -import io.github.wulkanowy.data.db.dao.MailboxDao -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.data.db.entities.Student -import javax.inject.Inject - -class GetMailboxByStudentUseCase @Inject constructor( - private val mailboxDao: MailboxDao, -) { - - suspend operator fun invoke(student: Student): Mailbox? { - return mailboxDao.loadAll(student.email) - .filterByStudent(student) - } - - private fun List.filterByStudent(student: Student): Mailbox? { - val normalizedStudentName = student.studentName.normalizeStudentName() - - return singleOrNull { - it.studentName.normalizeStudentName() == normalizedStudentName - } ?: singleOrNull { - it.studentName.normalizeStudentName() == normalizedStudentName - && it.schoolNameShort == student.schoolShortName - } ?: singleOrNull { - it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart() - } ?: singleOrNull { - it.studentName.getReversedName() == normalizedStudentName - } ?: singleOrNull { - it.studentName.getUnauthorizedVersion() == normalizedStudentName - } - } - - private fun String.normalizeStudentName(): String { - return trim().split(" ") - .filter { it.isNotBlank() } - .joinToString(" ") { part -> - part.lowercase().replaceFirstChar { it.uppercase() } - } - } - - private fun String.getFirstAndLastPart(): String { - val parts = normalizeStudentName().split(" ") - - val endParts = parts.filterIndexed { i, _ -> - i == 0 || parts.size - 1 == i - } - return endParts.joinToString(" ") - } - - private fun String.getReversedName(): String { - val parts = normalizeStudentName().split(" ") - - return parts - .asReversed() - .joinToString(" ") - } - - private fun String.getUnauthorizedVersion(): String { - return normalizeStudentName().split(" ") - .joinToString(" ") { - it.first() + "*".repeat(it.length - 1) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/HiltBroadcastReceiver.kt b/app/src/main/java/io/github/wulkanowy/services/HiltBroadcastReceiver.kt new file mode 100644 index 00000000..1e795d43 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/HiltBroadcastReceiver.kt @@ -0,0 +1,9 @@ +package io.github.wulkanowy.services + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent + +abstract class HiltBroadcastReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) {} +} diff --git a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt index 1729f100..cdf0c26a 100644 --- a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt +++ b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt @@ -15,7 +15,6 @@ import dagger.multibindings.IntoSet import io.github.wulkanowy.services.sync.channels.Channel import io.github.wulkanowy.services.sync.channels.DebugChannel import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel -import io.github.wulkanowy.services.sync.channels.NewAttendanceChannel import io.github.wulkanowy.services.sync.channels.NewConferencesChannel import io.github.wulkanowy.services.sync.channels.NewExamChannel import io.github.wulkanowy.services.sync.channels.NewGradesChannel @@ -24,7 +23,6 @@ import io.github.wulkanowy.services.sync.channels.NewMessagesChannel import io.github.wulkanowy.services.sync.channels.NewNotesChannel import io.github.wulkanowy.services.sync.channels.NewSchoolAnnouncementsChannel import io.github.wulkanowy.services.sync.channels.PushChannel -import io.github.wulkanowy.services.sync.channels.TimetableChangeChannel import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork import io.github.wulkanowy.services.sync.works.AttendanceWork @@ -169,12 +167,4 @@ abstract class ServicesModule { @Binds @IntoSet abstract fun provideUpcomingLessonsChannel(channel: UpcomingLessonsChannel): Channel - - @Binds - @IntoSet - abstract fun provideChangeTimetableChannel(channel: TimetableChangeChannel): Channel - - @Binds - @IntoSet - abstract fun provideNewAttendanceChannel(channel: NewAttendanceChannel): Channel } diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt index 01a583e1..406d91f5 100644 --- a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt +++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt @@ -1,7 +1,7 @@ package io.github.wulkanowy.services.alarm import android.app.PendingIntent -import android.content.BroadcastReceiver +import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.content.Context import android.content.Intent import android.os.Build @@ -10,38 +10,34 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.onResourceError -import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow +import io.github.wulkanowy.services.HiltBroadcastReceiver import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.utils.PendingIntentCompat +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.getCompatColor +import io.github.wulkanowy.utils.toLocalDateTime import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint -class TimetableNotificationReceiver : BroadcastReceiver() { +class TimetableNotificationReceiver : HiltBroadcastReceiver() { @Inject lateinit var studentRepository: StudentRepository - @Inject - lateinit var preferencesRepository: PreferencesRepository - companion object { const val NOTIFICATION_TYPE_CURRENT = 1 const val NOTIFICATION_TYPE_UPCOMING = 2 const val NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION = 3 - // FIXME only shows one notification even if there are multiple students. - // Probably want to fix after #721 is merged. - const val NOTIFICATION_ID = 2137 + const val NOTIFICATION_ID = "id" const val STUDENT_NAME = "student_name" const val STUDENT_ID = "student_id" @@ -56,33 +52,29 @@ class TimetableNotificationReceiver : BroadcastReceiver() { @OptIn(DelicateCoroutinesApi::class) override fun onReceive(context: Context, intent: Intent) { + super.onReceive(context, intent) Timber.d("Receiving intent... ${intent.toUri(0)}") - resourceFlow { - val showStudentName = !studentRepository.isOneUniqueStudent() + flowWithResource { val student = studentRepository.getCurrentStudent(false) val studentId = intent.getIntExtra(STUDENT_ID, 0) - - if (student.studentId == studentId) { - prepareNotification(context, intent, showStudentName) - } else { - Timber.d("Notification studentId($studentId) differs from current(${student.studentId})") - } - } - .onResourceError { Timber.e(it) } - .launchIn(GlobalScope) + if (student.studentId == studentId) prepareNotification(context, intent) + else Timber.d("Notification studentId($studentId) differs from current(${student.studentId})") + }.onEach { + if (it.status == Status.ERROR) Timber.e(it.error!!) + }.launchIn(GlobalScope) } - private fun prepareNotification(context: Context, intent: Intent, showStudentName: Boolean) { + private fun prepareNotification(context: Context, intent: Intent) { val type = intent.getIntExtra(LESSON_TYPE, 0) - val isPersistent = preferencesRepository.isUpcomingLessonsNotificationsPersistent + val notificationId = intent.getIntExtra(NOTIFICATION_ID, MainView.Section.TIMETABLE.id) if (type == NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION) { - return NotificationManagerCompat.from(context).cancel(NOTIFICATION_ID) + return NotificationManagerCompat.from(context).cancel(notificationId) } val studentId = intent.getIntExtra(STUDENT_ID, 0) - val studentName = intent.getStringExtra(STUDENT_NAME).takeIf { showStudentName } + val studentName = intent.getStringExtra(STUDENT_NAME) val subject = intent.getStringExtra(LESSON_TITLE) val room = intent.getStringExtra(LESSON_ROOM) @@ -93,68 +85,35 @@ class TimetableNotificationReceiver : BroadcastReceiver() { val nextSubject = intent.getStringExtra(LESSON_NEXT_TITLE) val nextRoom = intent.getStringExtra(LESSON_NEXT_ROOM) - Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: $start, student: $studentId") + Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: ${start.toLocalDateTime()}, student: $studentId") - val notificationTitleResId = - if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next - val notificationTitle = - context.getString(notificationTitleResId, "($room) $subject".removePrefix("()")) - - val nextLessonText = nextSubject?.let { - context.getString( - R.string.timetable_later, - "($nextRoom) $nextSubject".removePrefix("()") - ) - } - - showNotification( - context = context, - isPersistent = isPersistent, - studentName = studentName, - countDown = if (type == NOTIFICATION_TYPE_CURRENT) end else start, - timeout = end - start, - title = notificationTitle, - next = nextLessonText + showNotification(context, notificationId, studentName, + if (type == NOTIFICATION_TYPE_CURRENT) end else start, end - start, + context.getString(if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next, "($room) $subject".removePrefix("()")), + nextSubject?.let { context.getString(R.string.timetable_later, "($nextRoom) $nextSubject".removePrefix("()")) } ) } - private fun showNotification( - context: Context, - isPersistent: Boolean, - studentName: String?, - countDown: Long, - timeout: Long, - title: String, - next: String? - ) { - NotificationManagerCompat.from(context) - .notify(NOTIFICATION_ID, NotificationCompat.Builder(context, CHANNEL_ID) - .setContentTitle(title) - .setContentText(next) - .setAutoCancel(false) - .setWhen(countDown) - .setOngoing(isPersistent) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .apply { - if (Build.VERSION.SDK_INT >= N) setUsesChronometer(true) - } - .setTimeoutAfter(timeout) - .setSmallIcon(R.drawable.ic_stat_timetable) - .setColor(context.getCompatColor(R.color.colorPrimary)) - .setStyle(NotificationCompat.InboxStyle() - .addLine(next) - .also { inboxStyle -> - studentName?.let { inboxStyle.setSummaryText(it) } - }) - .setContentIntent( - PendingIntent.getActivity( - context, - NOTIFICATION_ID, - SplashActivity.getStartIntent(context, Destination.Timetable()), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) - ) - .build() - ) + private fun showNotification(context: Context, notificationId: Int, studentName: String?, countDown: Long, timeout: Long, title: String, next: String?) { + NotificationManagerCompat.from(context).notify(notificationId, NotificationCompat.Builder(context, CHANNEL_ID) + .setContentTitle(title) + .setContentText(next) + .setAutoCancel(false) + .setOngoing(true) + .setWhen(countDown) + .apply { + if (Build.VERSION.SDK_INT >= N) setUsesChronometer(true) + } + .setTimeoutAfter(timeout) + .setSmallIcon(R.drawable.ic_stat_timetable) + .setColor(context.getCompatColor(R.color.colorPrimary)) + .setStyle(NotificationCompat.InboxStyle().also { + it.setSummaryText(studentName) + it.addLine(next) + }) + .setContentIntent(PendingIntent.getActivity(context, MainView.Section.TIMETABLE.id, + MainActivity.getStartIntent(context, MainView.Section.TIMETABLE, true), FLAG_UPDATE_CURRENT)) + .build() + ) } } diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt index 42078d03..98bd93eb 100644 --- a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt +++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt @@ -3,9 +3,9 @@ package io.github.wulkanowy.services.alarm import android.app.AlarmManager import android.app.AlarmManager.RTC_WAKEUP import android.app.PendingIntent +import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.content.Context import android.content.Intent -import android.os.Build import androidx.core.app.AlarmManagerCompat import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.qualifiers.ApplicationContext @@ -25,15 +25,14 @@ import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companio import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.NOTIFICATION_TYPE_UPCOMING import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_ID import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_NAME +import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.DispatchersProvider -import io.github.wulkanowy.utils.PendingIntentCompat import io.github.wulkanowy.utils.nickOrName +import io.github.wulkanowy.utils.toTimestamp import kotlinx.coroutines.withContext import timber.log.Timber -import java.time.Duration.ofMinutes -import java.time.Instant -import java.time.Instant.now -import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalDateTime.now import javax.inject.Inject class TimetableNotificationSchedulerHelper @Inject constructor( @@ -43,67 +42,47 @@ class TimetableNotificationSchedulerHelper @Inject constructor( private val dispatchersProvider: DispatchersProvider, ) { - private fun getRequestCode(time: Instant, studentId: Int): Int = - (time.toEpochMilli() * studentId).toInt() + private fun getRequestCode(time: LocalDateTime, studentId: Int) = + (time.toTimestamp() * studentId).toInt() private fun getUpcomingLessonTime( index: Int, day: List, lesson: Timetable - ): Instant = day.getOrNull(index - 1)?.end ?: lesson.start.minus(ofMinutes(30)) + ) = day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30) suspend fun cancelScheduled(lessons: List, student: Student) { val studentId = student.studentId - withContext(dispatchersProvider.io) { + withContext(dispatchersProvider.backgroundThread) { lessons.sortedBy { it.start }.forEachIndexed { index, lesson -> val upcomingTime = getUpcomingLessonTime(index, lessons, lesson) cancelScheduledTo( - range = upcomingTime..lesson.start, - requestCode = getRequestCode(upcomingTime, studentId) - ) - cancelScheduledTo( - range = lesson.start..lesson.end, - requestCode = getRequestCode(lesson.start, studentId) + upcomingTime..lesson.start, + getRequestCode(upcomingTime, studentId) ) + cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId)) Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId") } } } - private fun cancelScheduledTo(range: ClosedRange, requestCode: Int) { + private fun cancelScheduledTo(range: ClosedRange, requestCode: Int) { if (now() in range) cancelNotification() - alarmManager.cancel( - PendingIntent.getBroadcast( - context, - requestCode, - Intent(), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) + PendingIntent.getBroadcast(context, requestCode, Intent(), FLAG_UPDATE_CURRENT) ) } fun cancelNotification() = - NotificationManagerCompat.from(context).cancel(NOTIFICATION_ID) + NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id) suspend fun scheduleNotifications(lessons: List, student: Student) { if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) { return cancelScheduled(lessons, student) } - if (!canScheduleExactAlarms()) { - Timber.w("Exact alarms are disabled by user") - preferencesRepository.isUpcomingLessonsNotificationsEnable = false - return - } - - if (lessons.firstOrNull()?.date?.isAfter(LocalDate.now().plusDays(2)) == true) { - Timber.d("Timetable notification scheduling skipped - lessons are too far") - return - } - - withContext(dispatchersProvider.io) { + withContext(dispatchersProvider.backgroundThread) { lessons.groupBy { it.date } .map { it.value.sortedBy { lesson -> lesson.start } } .map { it.filter { lesson -> lesson.isStudentPlan } } @@ -117,26 +96,26 @@ class TimetableNotificationSchedulerHelper @Inject constructor( if (lesson.start > now()) { scheduleBroadcast( - intent = intent, - studentId = student.studentId, - notificationType = NOTIFICATION_TYPE_UPCOMING, - time = getUpcomingLessonTime(index, active, lesson) + intent, + student.studentId, + NOTIFICATION_TYPE_UPCOMING, + getUpcomingLessonTime(index, active, lesson) ) } if (lesson.end > now()) { scheduleBroadcast( - intent = intent, - studentId = student.studentId, - notificationType = NOTIFICATION_TYPE_CURRENT, - time = lesson.start + intent, + student.studentId, + NOTIFICATION_TYPE_CURRENT, + lesson.start ) if (active.lastIndex == index) { scheduleBroadcast( - intent = intent, - studentId = student.studentId, - notificationType = NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, - time = lesson.end + intent, + student.studentId, + NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, + lesson.end ) } } @@ -150,8 +129,8 @@ class TimetableNotificationSchedulerHelper @Inject constructor( putExtra(STUDENT_ID, student.studentId) putExtra(STUDENT_NAME, student.nickOrName) putExtra(LESSON_ROOM, lesson.room) - putExtra(LESSON_START, lesson.start.toEpochMilli()) - putExtra(LESSON_END, lesson.end.toEpochMilli()) + putExtra(LESSON_START, lesson.start.toTimestamp()) + putExtra(LESSON_END, lesson.end.toTimestamp()) putExtra(LESSON_TITLE, lesson.subject) putExtra(LESSON_NEXT_TITLE, nextLesson?.subject) putExtra(LESSON_NEXT_ROOM, nextLesson?.room) @@ -162,32 +141,19 @@ class TimetableNotificationSchedulerHelper @Inject constructor( intent: Intent, studentId: Int, notificationType: Int, - time: Instant + time: LocalDateTime ) { - try { - AlarmManagerCompat.setExactAndAllowWhileIdle( - alarmManager, RTC_WAKEUP, time.toEpochMilli(), - PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also { - it.putExtra(LESSON_TYPE, notificationType) - }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE) - ) - Timber.d( - "TimetableNotification scheduled: type: $notificationType, subject: ${ - intent.getStringExtra(LESSON_TITLE) - }, start: $time, student: $studentId" - ) - } catch (e: Throwable) { - Timber.e(e) - } - } - - fun canScheduleExactAlarms(): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - try { - alarmManager.canScheduleExactAlarms() - } catch (e: Throwable) { - false - } - } else true + AlarmManagerCompat.setExactAndAllowWhileIdle( + alarmManager, RTC_WAKEUP, time.toTimestamp(), + PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also { + it.putExtra(NOTIFICATION_ID, MainView.Section.TIMETABLE.id) + it.putExtra(LESSON_TYPE, notificationType) + }, FLAG_UPDATE_CURRENT) + ) + Timber.d( + "TimetableNotification scheduled: type: $notificationType, subject: ${ + intent.getStringExtra(LESSON_TITLE) + }, start: $time, student: $studentId" + ) } } diff --git a/app/src/main/java/io/github/wulkanowy/services/piggyback/VulcanNotificationListenerService.kt b/app/src/main/java/io/github/wulkanowy/services/piggyback/VulcanNotificationListenerService.kt deleted file mode 100644 index 3c173495..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/piggyback/VulcanNotificationListenerService.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.services.piggyback - -import android.service.notification.NotificationListenerService -import android.service.notification.StatusBarNotification -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.services.sync.SyncManager -import javax.inject.Inject - -@AndroidEntryPoint -class VulcanNotificationListenerService : NotificationListenerService() { - - @Inject - lateinit var syncManager: SyncManager - - @Inject - lateinit var preferenceRepository: PreferencesRepository - - override fun onNotificationPosted(statusBarNotification: StatusBarNotification?) { - if (statusBarNotification?.packageName == "pl.edu.vulcan.hebe" && preferenceRepository.isNotificationPiggybackEnabled) { - syncManager.startOneTimeSyncWorker() - if (preferenceRepository.isNotificationPiggybackRemoveOriginalEnabled) { - cancelNotification(statusBarNotification.key) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt b/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt deleted file mode 100644 index 5e59aa54..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.services.shortcuts - -import android.content.Context -import android.content.Intent -import androidx.core.content.pm.ShortcutInfoCompat -import androidx.core.content.pm.ShortcutManagerCompat -import androidx.core.graphics.drawable.IconCompat -import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ShortcutsHelper @Inject constructor(@ApplicationContext private val context: Context) { - - fun initializeShortcuts() { - val shortcutsInfo = listOf( - ShortcutInfoCompat.Builder(context, "grade_shortcut") - .setShortLabel(context.getString(R.string.grade_title)) - .setLongLabel(context.getString(R.string.grade_title)) - .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_grade)) - .setIntent(SplashActivity.getStartIntent(context, Destination.Grade) - .apply { action = Intent.ACTION_VIEW }) - .build(), - - ShortcutInfoCompat.Builder(context, "attendance_shortcut") - .setShortLabel(context.getString(R.string.attendance_title)) - .setLongLabel(context.getString(R.string.attendance_title)) - .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_attendance)) - .setIntent(SplashActivity.getStartIntent(context, Destination.Attendance) - .apply { action = Intent.ACTION_VIEW }) - .build(), - - ShortcutInfoCompat.Builder(context, "exam_shortcut") - .setShortLabel(context.getString(R.string.exam_title)) - .setLongLabel(context.getString(R.string.exam_title)) - .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_exam)) - .setIntent(SplashActivity.getStartIntent(context, Destination.Exam) - .apply { action = Intent.ACTION_VIEW }) - .build(), - - ShortcutInfoCompat.Builder(context, "timetable_shortcut") - .setShortLabel(context.getString(R.string.timetable_title)) - .setLongLabel(context.getString(R.string.timetable_title)) - .setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_timetable)) - .setIntent(SplashActivity.getStartIntent(context, Destination.Timetable()) - .apply { action = Intent.ACTION_VIEW }) - .build() - ) - - shortcutsInfo.forEach { ShortcutManagerCompat.pushDynamicShortcut(context, it) } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt index e0a136f9..b94d97e3 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt @@ -4,12 +4,18 @@ import android.os.Build.VERSION.SDK_INT import android.os.Build.VERSION_CODES.O import androidx.core.app.NotificationManagerCompat import androidx.lifecycle.asFlow -import androidx.work.* import androidx.work.BackoffPolicy.EXPONENTIAL +import androidx.work.Constraints +import androidx.work.Data import androidx.work.ExistingPeriodicWorkPolicy.KEEP -import androidx.work.ExistingPeriodicWorkPolicy.UPDATE +import androidx.work.ExistingPeriodicWorkPolicy.REPLACE +import androidx.work.ExistingWorkPolicy import androidx.work.NetworkType.CONNECTED import androidx.work.NetworkType.UNMETERED +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkInfo +import androidx.work.WorkManager import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY import io.github.wulkanowy.data.repositories.PreferencesRepository @@ -51,39 +57,27 @@ class SyncManager @Inject constructor( fun startPeriodicSyncWorker(restart: Boolean = false) { if (preferencesRepository.isServiceEnabled && !now().isHolidays) { - val serviceInterval = preferencesRepository.servicesInterval - - workManager.enqueueUniquePeriodicWork( - SyncWorker::class.java.simpleName, if (restart) UPDATE else KEEP, - PeriodicWorkRequestBuilder(serviceInterval, MINUTES) + workManager.enqueueUniquePeriodicWork(SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP, + PeriodicWorkRequestBuilder(preferencesRepository.servicesInterval, MINUTES) .setInitialDelay(10, MINUTES) .setBackoffCriteria(EXPONENTIAL, 30, MINUTES) - .setConstraints( - Constraints.Builder() - .setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) UNMETERED else CONNECTED) - .build() - ) - .build() - ) + .setConstraints(Constraints.Builder() + .setRequiredNetworkType(if (preferencesRepository.isServicesOnlyWifi) UNMETERED else CONNECTED) + .build()) + .build()) } } - // if quiet, no notifications will be sent - fun startOneTimeSyncWorker(quiet: Boolean = false): Flow { + fun startOneTimeSyncWorker(): Flow { val work = OneTimeWorkRequestBuilder() .setInputData( Data.Builder() - .putBoolean("quiet", quiet) .putBoolean("one_time", true) .build() ) .build() - workManager.enqueueUniqueWork( - "${SyncWorker::class.java.simpleName}_one_time", - ExistingWorkPolicy.REPLACE, - work - ) + workManager.enqueueUniqueWork("${SyncWorker::class.java.simpleName}_one_time", ExistingWorkPolicy.REPLACE, work) return workManager.getWorkInfoByIdLiveData(work.id).asFlow() } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt index 5dddd9a7..ea1f79cb 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -19,11 +19,11 @@ import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException import io.github.wulkanowy.services.sync.channels.DebugChannel import io.github.wulkanowy.services.sync.works.Work -import io.github.wulkanowy.utils.DispatchersProvider import io.github.wulkanowy.utils.getCompatColor -import kotlinx.coroutines.withContext +import kotlinx.coroutines.coroutineScope import timber.log.Timber -import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId import kotlin.random.Random @HiltWorker @@ -34,66 +34,49 @@ class SyncWorker @AssistedInject constructor( private val semesterRepository: SemesterRepository, private val works: Set<@JvmSuppressWildcards Work>, private val preferencesRepository: PreferencesRepository, - private val notificationManager: NotificationManagerCompat, - private val dispatchersProvider: DispatchersProvider + private val notificationManager: NotificationManagerCompat ) : CoroutineWorker(appContext, workerParameters) { - override suspend fun doWork(): Result = withContext(dispatchersProvider.io) { + override suspend fun doWork() = coroutineScope { Timber.i("SyncWorker is starting") - if (!studentRepository.isCurrentStudentSet()) return@withContext Result.failure() + if (!studentRepository.isCurrentStudentSet()) return@coroutineScope Result.failure() - val (student, semester) = try { - val student = studentRepository.getCurrentStudent() - val semester = semesterRepository.getCurrentSemester(student, true) - student to semester - } catch (e: Throwable) { - return@withContext getResultFromErrors(listOf(e)) - } + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student, true) val exceptions = works.mapNotNull { work -> try { Timber.i("${work::class.java.simpleName} is starting") - work.doWork(student, semester, isNotificationsEnabled()) + work.doWork(student, semester) Timber.i("${work::class.java.simpleName} result: Success") + preferencesRepository.lasSyncDate = LocalDateTime.now(ZoneId.systemDefault()) null } catch (e: Throwable) { Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred") - if (e is FeatureDisabledException || e is FeatureNotAvailableException) { - null - } else { + if (e is FeatureDisabledException || e is FeatureNotAvailableException) null + else { Timber.e(e) e } } } - val result = getResultFromErrors(exceptions) + val result = when { + exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> { + Result.failure( + Data.Builder() + .putString("error", exceptions.map { it.stackTraceToString() }.toString()) + .build() + ) + } + exceptions.isNotEmpty() -> Result.retry() + else -> Result.success() + } if (preferencesRepository.isDebugNotificationEnable) notify(result) Timber.i("SyncWorker result: $result") - return@withContext result - } - - private fun isNotificationsEnabled(): Boolean { - val quiet = inputData.getBoolean("quiet", false) - return preferencesRepository.isNotificationsEnable && !quiet - } - - private fun getResultFromErrors(errors: List): Result = when { - errors.isNotEmpty() && inputData.getBoolean("one_time", false) -> { - Result.failure( - Data.Builder() - .putString("error_message", errors.joinToString { it.message.toString() }) - .putString("error_stack", errors.map { it.stackTraceToString() }.toString()) - .build() - ) - } - errors.isNotEmpty() -> Result.retry() - else -> { - preferencesRepository.lasSyncDate = Instant.now() - Result.success() - } + result } private fun notify(result: Result) { diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewAttendanceChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewAttendanceChannel.kt deleted file mode 100644 index 3110099e..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/channels/NewAttendanceChannel.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.services.sync.channels - -import android.annotation.TargetApi -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.content.Context -import androidx.core.app.NotificationManagerCompat -import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.R -import javax.inject.Inject - -@TargetApi(26) -class NewAttendanceChannel @Inject constructor( - private val notificationManager: NotificationManagerCompat, - @ApplicationContext private val context: Context -) : Channel { - - companion object { - const val CHANNEL_ID = "new_attendance_channel" - } - - override fun create() { - notificationManager.createNotificationChannel( - NotificationChannel( - CHANNEL_ID, - context.getString(R.string.channel_new_attendance), - NotificationManager.IMPORTANCE_HIGH - ) - .apply { - enableLights(true) - enableVibration(true) - lockscreenVisibility = Notification.VISIBILITY_PUBLIC - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/TimetableChangeChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/TimetableChangeChannel.kt deleted file mode 100644 index 10dd3e00..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/channels/TimetableChangeChannel.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.services.sync.channels - -import android.annotation.TargetApi -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.content.Context -import androidx.core.app.NotificationManagerCompat -import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.R -import javax.inject.Inject - -@TargetApi(26) -class TimetableChangeChannel @Inject constructor( - private val notificationManager: NotificationManagerCompat, - @ApplicationContext private val context: Context -) : Channel { - - companion object { - const val CHANNEL_ID = "change_timetable_channel" - } - - override fun create() { - notificationManager.createNotificationChannel( - NotificationChannel( - CHANNEL_ID, - context.getString(R.string.channel_change_timetable), - NotificationManager.IMPORTANCE_HIGH - ) - .apply { - enableLights(true) - enableVibration(true) - lockscreenVisibility = Notification.VISIBILITY_PUBLIC - }) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt deleted file mode 100644 index dadb68c5..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt +++ /dev/null @@ -1,179 +0,0 @@ -package io.github.wulkanowy.services.sync.notifications - -import android.annotation.SuppressLint -import android.app.PendingIntent -import android.content.Context -import android.os.Build -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat -import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Notification -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.GroupNotificationData -import io.github.wulkanowy.data.pojos.NotificationData -import io.github.wulkanowy.data.repositories.NotificationRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.utils.PendingIntentCompat -import io.github.wulkanowy.utils.getCompatBitmap -import io.github.wulkanowy.utils.getCompatColor -import io.github.wulkanowy.utils.nickOrName -import java.time.Instant -import javax.inject.Inject -import kotlin.random.Random - -class AppNotificationManager @Inject constructor( - private val notificationManager: NotificationManagerCompat, - @ApplicationContext private val context: Context, - private val studentRepository: StudentRepository, - private val notificationRepository: NotificationRepository -) { - - @SuppressLint("InlinedApi") - suspend fun sendSingleNotification( - notificationData: NotificationData, - notificationType: NotificationType, - student: Student - ) { - val notification = NotificationCompat.Builder(context, notificationType.channel) - .setLargeIcon(context.getCompatBitmap(notificationType.icon, R.color.colorPrimary)) - .setSmallIcon(R.drawable.ic_stat_all) - .setAutoCancel(true) - .setDefaults(NotificationCompat.DEFAULT_ALL) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setColor(context.getCompatColor(R.color.colorPrimary)) - .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) - .setContentIntent( - PendingIntent.getActivity( - context, - Random.nextInt(), - SplashActivity.getStartIntent(context, notificationData.destination), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) - ) - .setContentTitle(notificationData.title) - .setContentText(notificationData.content) - .setStyle( - NotificationCompat.BigTextStyle() - .bigText(notificationData.content) - .also { builder -> - if (!studentRepository.isOneUniqueStudent()) { - builder.setSummaryText(student.nickOrName) - } - } - ) - .build() - - notificationManager.notify(Random.nextInt(), notification) - saveNotification(notificationData, notificationType, student) - } - - @SuppressLint("InlinedApi") - suspend fun sendMultipleNotifications( - groupNotificationData: GroupNotificationData, - student: Student - ) { - val notificationType = groupNotificationData.type - val groupType = notificationType.channel - val group = "${groupType}_${student.id}" - - sendSummaryNotification(groupNotificationData, group, student) - - groupNotificationData.notificationDataList.forEach { notificationData -> - val notification = NotificationCompat.Builder(context, notificationType.channel) - .setLargeIcon(context.getCompatBitmap(notificationType.icon, R.color.colorPrimary)) - .setSmallIcon(R.drawable.ic_stat_all) - .setAutoCancel(true) - .setDefaults(NotificationCompat.DEFAULT_ALL) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setColor(context.getCompatColor(R.color.colorPrimary)) - .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) - .setContentIntent( - PendingIntent.getActivity( - context, - Random.nextInt(), - SplashActivity.getStartIntent(context, notificationData.destination), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) - ) - .setContentTitle(notificationData.title) - .setContentText(notificationData.content) - .setStyle( - NotificationCompat.BigTextStyle() - .bigText(notificationData.content) - .also { builder -> - if (!studentRepository.isOneUniqueStudent()) { - builder.setSummaryText(student.nickOrName) - } - } - ) - .setGroup(group) - .build() - - notificationManager.notify(Random.nextInt(), notification) - saveNotification(notificationData, groupNotificationData.type, student) - } - } - - private suspend fun sendSummaryNotification( - groupNotificationData: GroupNotificationData, - group: String, - student: Student - ) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return - - val summaryNotification = - NotificationCompat.Builder(context, groupNotificationData.type.channel) - .setContentTitle(groupNotificationData.title) - .setContentText(groupNotificationData.content) - .setSmallIcon(groupNotificationData.type.icon) - .setAutoCancel(true) - .setDefaults(NotificationCompat.DEFAULT_ALL) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setColor(context.getCompatColor(R.color.colorPrimary)) - .setStyle( - NotificationCompat.InboxStyle() - .also { builder -> - if (!studentRepository.isOneUniqueStudent()) { - builder.setSummaryText(student.nickOrName) - } - groupNotificationData.notificationDataList.forEach { - builder.addLine(it.content) - } - } - ) - .setContentIntent( - PendingIntent.getActivity( - context, - Random.nextInt(), - SplashActivity.getStartIntent(context, groupNotificationData.destination), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) - ) - .setLocalOnly(true) - .setGroup(group) - .setGroupSummary(true) - .build() - - val groupId = student.id * 100 + groupNotificationData.type.ordinal - notificationManager.notify(groupId.toInt(), summaryNotification) - } - - private suspend fun saveNotification( - notificationData: NotificationData, - notificationType: NotificationType, - student: Student - ) { - val notificationEntity = Notification( - studentId = student.id, - title = notificationData.title, - content = notificationData.content, - destination = notificationData.destination, - type = notificationType, - date = Instant.now(), - ) - - notificationRepository.saveNotification(notificationEntity) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/BaseNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/BaseNotification.kt new file mode 100644 index 00000000..8c9cb471 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/BaseNotification.kt @@ -0,0 +1,102 @@ +package io.github.wulkanowy.services.sync.notifications + +import android.app.PendingIntent +import android.content.Context +import android.os.Build +import androidx.annotation.PluralsRes +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.pojos.MultipleNotifications +import io.github.wulkanowy.data.pojos.Notification +import io.github.wulkanowy.data.pojos.OneNotification +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.utils.getCompatBitmap +import io.github.wulkanowy.utils.getCompatColor +import io.github.wulkanowy.utils.nickOrName +import kotlin.random.Random + +abstract class BaseNotification( + private val context: Context, + private val notificationManager: NotificationManagerCompat, +) { + + protected fun sendNotification(notification: Notification, student: Student) = + when (notification) { + is OneNotification -> sendOneNotification(notification, student) + is MultipleNotifications -> sendMultipleNotifications(notification, student) + } + + private fun sendOneNotification(notification: OneNotification, student: Student?) { + notificationManager.notify( + Random.nextInt(Int.MAX_VALUE), + getNotificationBuilder(notification).apply { + val content = context.getString( + notification.contentStringRes, + *notification.contentValues.toTypedArray() + ) + setContentTitle(context.getString(notification.titleStringRes)) + setContentText(content) + setStyle( + NotificationCompat.BigTextStyle() + .setSummaryText(student?.nickOrName) + .bigText(content) + ) + }.build() + ) + } + + private fun sendMultipleNotifications(notification: MultipleNotifications, student: Student) { + val group = notification.type.group + student.id + val groupId = student.id * 100 + notification.type.ordinal + + notification.lines.forEach { item -> + notificationManager.notify( + Random.nextInt(Int.MAX_VALUE), + getNotificationBuilder(notification).apply { + setContentTitle(getQuantityString(notification.titleStringRes, 1)) + setContentText(item) + setStyle( + NotificationCompat.BigTextStyle() + .setSummaryText(student.nickOrName) + .bigText(item) + ) + setGroup(group) + }.build() + ) + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return + + notificationManager.notify( + groupId.toInt(), + getNotificationBuilder(notification).apply { + setSmallIcon(notification.icon) + setGroup(group) + setStyle(NotificationCompat.InboxStyle().setSummaryText(student.nickOrName)) + setGroupSummary(true) + }.build() + ) + } + + private fun getNotificationBuilder(notification: Notification) = NotificationCompat + .Builder(context, notification.type.channel) + .setLargeIcon(context.getCompatBitmap(notification.icon, R.color.colorPrimary)) + .setSmallIcon(R.drawable.ic_stat_all) + .setAutoCancel(true) + .setDefaults(NotificationCompat.DEFAULT_ALL) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setColor(context.getCompatColor(R.color.colorPrimary)) + .setContentIntent( + PendingIntent.getActivity( + context, notification.startMenu.id, + MainActivity.getStartIntent(context, notification.startMenu, true), + PendingIntent.FLAG_UPDATE_CURRENT + ) + ) + + private fun getQuantityString(@PluralsRes id: Int, value: Int): String { + return context.resources.getQuantityString(id, value, value) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt deleted file mode 100644 index 43ae1fea..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt +++ /dev/null @@ -1,120 +0,0 @@ -package io.github.wulkanowy.services.sync.notifications - -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.data.pojos.GroupNotificationData -import io.github.wulkanowy.data.pojos.NotificationData -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.utils.getPlural -import io.github.wulkanowy.utils.toFormattedString -import java.time.Instant -import java.time.LocalDate -import javax.inject.Inject - -class ChangeTimetableNotification @Inject constructor( - private val appNotificationManager: AppNotificationManager, - @ApplicationContext private val context: Context, -) { - - suspend fun notify(items: List, student: Student) { - val currentTime = Instant.now() - val changedLessons = items.filter { (it.canceled || it.changes) && it.start > currentTime } - val lessonsByDate = changedLessons.groupBy { it.date } - val notificationDataList = lessonsByDate - .flatMap { (date, lessons) -> - getNotificationContents(date, lessons).map { - NotificationData( - title = context.getPlural( - R.plurals.timetable_notify_new_items_title, - 1 - ), - content = it, - destination = Destination.Timetable(date) - ) - } - } - .ifEmpty { return } - - val groupNotificationData = GroupNotificationData( - notificationDataList = notificationDataList, - title = context.getPlural( - R.plurals.timetable_notify_new_items_title, - changedLessons.size - ), - content = context.getPlural( - R.plurals.timetable_notify_new_items_group, - changedLessons.size, - changedLessons.size - ), - destination = Destination.Timetable(lessonsByDate.toSortedMap().firstKey()), - type = NotificationType.CHANGE_TIMETABLE - ) - - appNotificationManager.sendMultipleNotifications(groupNotificationData, student) - } - - private fun getNotificationContents(date: LocalDate, lessons: List): List { - val formattedDate = date.toFormattedString("EEE dd.MM") - - return if (lessons.size > 2) { - listOf( - context.getPlural( - R.plurals.timetable_notify_new_items, - lessons.size, - formattedDate, - lessons.size, - ) - ) - } else { - lessons.map { - buildString { - append( - context.getString( - R.string.timetable_notify_lesson, - formattedDate, - it.number, - it.subject - ) - ) - if (it.roomOld.isNotBlank()) { - appendLine() - append( - context.getString( - R.string.timetable_notify_change_room, - it.roomOld, - it.room - ) - ) - } - if (it.teacherOld.isNotBlank() && it.teacher != it.teacherOld) { - appendLine() - append( - context.getString( - R.string.timetable_notify_change_teacher, - it.teacherOld, - it.teacher - ) - ) - } - if (it.subjectOld.isNotBlank()) { - appendLine() - append( - context.getString( - R.string.timetable_notify_change_subject, - it.subjectOld, - it.subject - ) - ) - } - if (it.info.isNotBlank()) { - appendLine() - append(it.info) - } - } - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt deleted file mode 100644 index 99473a8e..00000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.services.sync.notifications - -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.GroupNotificationData -import io.github.wulkanowy.data.pojos.NotificationData -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.utils.descriptionRes -import io.github.wulkanowy.utils.getPlural -import io.github.wulkanowy.utils.toFormattedString -import javax.inject.Inject - -class NewAttendanceNotification @Inject constructor( - private val appNotificationManager: AppNotificationManager, - @ApplicationContext private val context: Context -) { - - suspend fun notify(items: List, student: Student) { - val lines = items.filterNot { it.presence || it.name == "UNKNOWN" } - .map { - val lesson = it.subject.ifBlank { "Lekcja ${it.number}" } - val description = context.getString(it.descriptionRes) - "${it.date.toFormattedString("dd.MM")} - $lesson: $description" - } - .ifEmpty { return } - - val notificationDataList = lines.map { - NotificationData( - title = context.getPlural(R.plurals.attendance_notify_new_items_title, 1), - content = it, - destination = Destination.Attendance - ) - } - - val groupNotificationData = GroupNotificationData( - notificationDataList = notificationDataList, - title = context.getPlural( - R.plurals.attendance_notify_new_items_title, - notificationDataList.size - ), - content = context.getPlural( - R.plurals.attendance_notify_new_items, - notificationDataList.size, - notificationDataList.size - ), - destination = Destination.Attendance, - type = NotificationType.NEW_ATTENDANCE - ) - - appNotificationManager.sendMultipleNotifications(groupNotificationData, student) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt index 92977ebb..fda2922f 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt @@ -1,52 +1,38 @@ package io.github.wulkanowy.services.sync.notifications import android.content.Context +import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Conference import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.GroupNotificationData -import io.github.wulkanowy.data.pojos.NotificationData -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.data.pojos.MultipleNotifications +import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.toFormattedString -import java.time.Instant +import java.time.LocalDateTime import javax.inject.Inject class NewConferenceNotification @Inject constructor( - private val appNotificationManager: AppNotificationManager, - @ApplicationContext private val context: Context -) { + @ApplicationContext private val context: Context, + notificationManager: NotificationManagerCompat, +) : BaseNotification(context, notificationManager) { - suspend fun notify(items: List, student: Student) { - val today = Instant.now() - val lines = items.filter { !it.date.isBefore(today) } - .map { - "${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}" - } - .ifEmpty { return } + fun notify(items: List, student: Student) { + val today = LocalDateTime.now() + val lines = items.filter { !it.date.isBefore(today) }.map { + "${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}" + }.ifEmpty { return } - val notificationDataList = lines.map { - NotificationData( - title = context.getPlural(R.plurals.conference_notify_new_item_title, 1), - content = it, - destination = Destination.Conference - ) - } - - val groupNotificationData = GroupNotificationData( - notificationDataList = notificationDataList, - title = context.getPlural(R.plurals.conference_notify_new_item_title, lines.size), - content = context.getPlural( - R.plurals.conference_notify_new_items, - lines.size, - lines.size - ), - destination = Destination.Conference, - type = NotificationType.NEW_CONFERENCE + val notification = MultipleNotifications( + type = NotificationType.NEW_CONFERENCE, + icon = R.drawable.ic_more_conferences, + titleStringRes = R.plurals.conference_notify_new_item_title, + contentStringRes = R.plurals.conference_notify_new_items, + summaryStringRes = R.plurals.conference_number_item, + startMenu = MainView.Section.CONFERENCE, + lines = lines ) - appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + sendNotification(notification, student) } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewExamNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewExamNotification.kt index 125bbf92..d493c4d2 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewExamNotification.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewExamNotification.kt @@ -1,52 +1,38 @@ package io.github.wulkanowy.services.sync.notifications import android.content.Context +import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.GroupNotificationData -import io.github.wulkanowy.data.pojos.NotificationData -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.data.pojos.MultipleNotifications +import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.toFormattedString import java.time.LocalDate import javax.inject.Inject class NewExamNotification @Inject constructor( - private val appNotificationManager: AppNotificationManager, - @ApplicationContext private val context: Context -) { + @ApplicationContext private val context: Context, + notificationManager: NotificationManagerCompat, +) : BaseNotification(context, notificationManager) { - suspend fun notify(items: List, student: Student) { + fun notify(items: List, student: Student) { val today = LocalDate.now() - val lines = items.filter { !it.date.isBefore(today) } - .map { - "${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.description}" - } - .ifEmpty { return } + val lines = items.filter { !it.date.isBefore(today) }.map { + "${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.description}" + }.ifEmpty { return } - val notificationDataList = lines.map { - NotificationData( - title = context.getPlural(R.plurals.exam_notify_new_item_title, 1), - content = it, - destination = Destination.Exam, - ) - } - - val groupNotificationData = GroupNotificationData( - notificationDataList = notificationDataList, - title = context.getPlural(R.plurals.exam_notify_new_item_title, lines.size), - content = context.getPlural( - R.plurals.exam_notify_new_item_content, - lines.size, - lines.size - ), - destination = Destination.Exam, - type = NotificationType.NEW_EXAM + val notification = MultipleNotifications( + type = NotificationType.NEW_EXAM, + icon = R.drawable.ic_main_exam, + titleStringRes = R.plurals.exam_notify_new_item_title, + contentStringRes = R.plurals.exam_notify_new_item_content, + summaryStringRes = R.plurals.exam_number_item, + startMenu = MainView.Section.EXAM, + lines = lines ) - appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + sendNotification(notification, student) } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt index 9b49ed17..415ba343 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt @@ -1,91 +1,66 @@ package io.github.wulkanowy.services.sync.notifications import android.content.Context +import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.GroupNotificationData -import io.github.wulkanowy.data.pojos.NotificationData -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.data.pojos.MultipleNotifications +import io.github.wulkanowy.ui.modules.main.MainView import javax.inject.Inject class NewGradeNotification @Inject constructor( - private val appNotificationManager: AppNotificationManager, - @ApplicationContext private val context: Context -) { + @ApplicationContext private val context: Context, + notificationManager: NotificationManagerCompat, +) : BaseNotification(context, notificationManager) { - suspend fun notifyDetails(items: List, student: Student) { - val notificationDataList = items.map { - NotificationData( - title = context.getPlural(R.plurals.grade_new_items, 1), - content = buildString { - append("${it.subject}: ${it.entry}") - if (it.comment.isNotBlank()) append(" (${it.comment})") - }, - destination = Destination.Grade, - ) - } - - val groupNotificationData = GroupNotificationData( - notificationDataList = notificationDataList, - title = context.getPlural(R.plurals.grade_new_items, items.size), - content = context.getPlural(R.plurals.grade_notify_new_items, items.size, items.size), - destination = Destination.Grade, - type = NotificationType.NEW_GRADE_DETAILS + fun notifyDetails(items: List, student: Student) { + val notification = MultipleNotifications( + type = NotificationType.NEW_GRADE_DETAILS, + icon = R.drawable.ic_stat_grade, + titleStringRes = R.plurals.grade_new_items, + contentStringRes = R.plurals.grade_notify_new_items, + summaryStringRes = R.plurals.grade_number_item, + startMenu = MainView.Section.GRADE, + lines = items.map { + "${it.subject}: ${it.entry}" + } ) - appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + sendNotification(notification, student) } - suspend fun notifyPredicted(items: List, student: Student) { - val notificationDataList = items.map { - NotificationData( - title = context.getPlural(R.plurals.grade_new_items_predicted, 1), - content = "${it.subject}: ${it.predictedGrade}", - destination = Destination.Grade, - ) - } - - val groupNotificationData = GroupNotificationData( - notificationDataList = notificationDataList, - title = context.getPlural(R.plurals.grade_new_items_predicted, items.size), - content = context.getPlural( - R.plurals.grade_notify_new_items_predicted, - items.size, - items.size - ), - destination = Destination.Grade, - type = NotificationType.NEW_GRADE_PREDICTED + fun notifyPredicted(items: List, student: Student) { + val notification = MultipleNotifications( + type = NotificationType.NEW_GRADE_PREDICTED, + icon = R.drawable.ic_stat_grade, + titleStringRes = R.plurals.grade_new_items_predicted, + contentStringRes = R.plurals.grade_notify_new_items_predicted, + summaryStringRes = R.plurals.grade_number_item, + startMenu = MainView.Section.GRADE, + lines = items.map { + "${it.subject}: ${it.predictedGrade}" + } ) - appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + sendNotification(notification, student) } - suspend fun notifyFinal(items: List, student: Student) { - val notificationDataList = items.map { - NotificationData( - title = context.getPlural(R.plurals.grade_new_items_final, 1), - content = "${it.subject}: ${it.finalGrade}", - destination = Destination.Grade, - ) - } - - val groupNotificationData = GroupNotificationData( - notificationDataList = notificationDataList, - title = context.getPlural(R.plurals.grade_new_items_final, items.size), - content = context.getPlural( - R.plurals.grade_notify_new_items_final, - items.size, - items.size - ), - destination = Destination.Grade, - type = NotificationType.NEW_GRADE_FINAL + fun notifyFinal(items: List, student: Student) { + val notification = MultipleNotifications( + type = NotificationType.NEW_GRADE_FINAL, + icon = R.drawable.ic_stat_grade, + titleStringRes = R.plurals.grade_new_items_final, + contentStringRes = R.plurals.grade_notify_new_items_final, + summaryStringRes = R.plurals.grade_number_item, + startMenu = MainView.Section.GRADE, + lines = items.map { + "${it.subject}: ${it.finalGrade}" + } ) - appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + sendNotification(notification, student) } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewHomeworkNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewHomeworkNotification.kt index 856c5158..fe973cad 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewHomeworkNotification.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewHomeworkNotification.kt @@ -1,52 +1,38 @@ package io.github.wulkanowy.services.sync.notifications import android.content.Context +import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.GroupNotificationData -import io.github.wulkanowy.data.pojos.NotificationData -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.data.pojos.MultipleNotifications +import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.toFormattedString import java.time.LocalDate import javax.inject.Inject class NewHomeworkNotification @Inject constructor( - private val appNotificationManager: AppNotificationManager, - @ApplicationContext private val context: Context -) { + @ApplicationContext private val context: Context, + notificationManager: NotificationManagerCompat, +) : BaseNotification(context, notificationManager) { - suspend fun notify(items: List, student: Student) { + fun notify(items: List, student: Student) { val today = LocalDate.now() - val lines = items.filter { !it.date.isBefore(today) } - .map { - "${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.content}" - } - .ifEmpty { return } + val lines = items.filter { !it.date.isBefore(today) }.map { + "${it.date.toFormattedString("dd.MM")} - ${it.subject}: ${it.content}" + }.ifEmpty { return } - val notificationDataList = lines.map { - NotificationData( - title = context.getPlural(R.plurals.homework_notify_new_item_title, 1), - content = it, - destination = Destination.Homework, - ) - } - - val groupNotificationData = GroupNotificationData( - title = context.getPlural(R.plurals.homework_notify_new_item_title, lines.size), - content = context.getPlural( - R.plurals.homework_notify_new_item_content, - lines.size, - lines.size - ), - destination = Destination.Homework, + val notification = MultipleNotifications( type = NotificationType.NEW_HOMEWORK, - notificationDataList = notificationDataList + icon = R.drawable.ic_more_homework, + titleStringRes = R.plurals.homework_notify_new_item_title, + contentStringRes = R.plurals.homework_notify_new_item_content, + summaryStringRes = R.plurals.homework_number_item, + startMenu = MainView.Section.HOMEWORK, + lines = lines ) - appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + sendNotification(notification, student) } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewLuckyNumberNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewLuckyNumberNotification.kt index bbe9b8a1..95156c45 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewLuckyNumberNotification.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewLuckyNumberNotification.kt @@ -1,34 +1,30 @@ package io.github.wulkanowy.services.sync.notifications import android.content.Context +import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.NotificationData -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity +import io.github.wulkanowy.data.pojos.OneNotification +import io.github.wulkanowy.ui.modules.main.MainView import javax.inject.Inject class NewLuckyNumberNotification @Inject constructor( - private val appNotificationManager: AppNotificationManager, - @ApplicationContext private val context: Context -) { + @ApplicationContext private val context: Context, + notificationManager: NotificationManagerCompat, +) : BaseNotification(context, notificationManager) { - suspend fun notify(item: LuckyNumber, student: Student) { - val notificationData = NotificationData( - title = context.getString(R.string.lucky_number_notify_new_item_title), - content = context.getString( - R.string.lucky_number_notify_new_item, - item.luckyNumber.toString() - ), - destination = Destination.LuckyNumber + fun notify(item: LuckyNumber, student: Student) { + val notification = OneNotification( + type = NotificationType.NEW_LUCKY_NUMBER, + icon = R.drawable.ic_stat_luckynumber, + titleStringRes = R.string.lucky_number_notify_new_item_title, + contentStringRes = R.string.lucky_number_notify_new_item, + startMenu = MainView.Section.LUCKY_NUMBER, + contentValues = listOf(item.luckyNumber.toString()) ) - appNotificationManager.sendSingleNotification( - notificationData = notificationData, - notificationType = NotificationType.NEW_LUCKY_NUMBER, - student = student - ) + sendNotification(notification, student) } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt index 45523d51..fc364198 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt @@ -1,38 +1,33 @@ package io.github.wulkanowy.services.sync.notifications import android.content.Context +import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.GroupNotificationData -import io.github.wulkanowy.data.pojos.NotificationData -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.data.pojos.MultipleNotifications +import io.github.wulkanowy.ui.modules.main.MainView import javax.inject.Inject class NewMessageNotification @Inject constructor( - private val appNotificationManager: AppNotificationManager, - @ApplicationContext private val context: Context -) { + @ApplicationContext private val context: Context, + notificationManager: NotificationManagerCompat, +) : BaseNotification(context, notificationManager) { - suspend fun notify(items: List, student: Student) { - val notificationDataList = items.map { - NotificationData( - title = context.getPlural(R.plurals.message_new_items, 1), - content = "${it.correspondents}: ${it.subject}", - destination = Destination.Message, - ) - } - - val groupNotificationData = GroupNotificationData( - notificationDataList = notificationDataList, - title = context.getPlural(R.plurals.message_new_items, items.size), - content = context.getPlural(R.plurals.message_notify_new_items, items.size, items.size), - destination = Destination.Message, - type = NotificationType.NEW_MESSAGE + fun notify(items: List, student: Student) { + val notification = MultipleNotifications( + type = NotificationType.NEW_MESSAGE, + icon = R.drawable.ic_stat_message, + titleStringRes = R.plurals.message_new_items, + contentStringRes = R.plurals.message_notify_new_items, + summaryStringRes = R.plurals.message_number_item, + startMenu = MainView.Section.MESSAGE, + lines = items.map { + "${it.sender}: ${it.subject}" + } ) - appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + sendNotification(notification, student) } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewNoteNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewNoteNotification.kt index dae7d433..f355341b 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewNoteNotification.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewNoteNotification.kt @@ -1,46 +1,46 @@ package io.github.wulkanowy.services.sync.notifications import android.content.Context +import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.GroupNotificationData -import io.github.wulkanowy.data.pojos.NotificationData +import io.github.wulkanowy.data.pojos.MultipleNotifications import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.ui.modules.main.MainView import javax.inject.Inject class NewNoteNotification @Inject constructor( - private val appNotificationManager: AppNotificationManager, - @ApplicationContext private val context: Context -) { + @ApplicationContext private val context: Context, + notificationManager: NotificationManagerCompat, +) : BaseNotification(context, notificationManager) { - suspend fun notify(items: List, student: Student) { - val notificationDataList = items.map { - val titleRes = when (NoteCategory.getByValue(it.categoryType)) { + fun notify(items: List, student: Student) { + val notification = MultipleNotifications( + type = NotificationType.NEW_NOTE, + icon = R.drawable.ic_stat_note, + titleStringRes = when (NoteCategory.getByValue(items.first().categoryType)) { NoteCategory.POSITIVE -> R.plurals.praise_new_items NoteCategory.NEUTRAL -> R.plurals.neutral_note_new_items else -> R.plurals.note_new_items + }, + contentStringRes = when (NoteCategory.getByValue(items.first().categoryType)) { + NoteCategory.POSITIVE -> R.plurals.praise_notify_new_items + NoteCategory.NEUTRAL -> R.plurals.neutral_note_notify_new_items + else -> R.plurals.note_notify_new_items + }, + summaryStringRes = when (NoteCategory.getByValue(items.first().categoryType)) { + NoteCategory.POSITIVE -> R.plurals.praise_number_item + NoteCategory.NEUTRAL -> R.plurals.neutral_note_number_item + else -> R.plurals.note_number_item + }, + startMenu = MainView.Section.NOTE, + lines = items.map { + "${it.teacher}: ${it.category}" } - - NotificationData( - title = context.getPlural(titleRes, 1), - content = "${it.teacher}: ${it.category}", - destination = Destination.Note, - ) - } - - val groupNotificationData = GroupNotificationData( - notificationDataList = notificationDataList, - destination = Destination.Note, - title = context.getPlural(R.plurals.note_new_items, items.size), - content = context.getPlural(R.plurals.note_notify_new_items, items.size, items.size), - type = NotificationType.NEW_NOTE ) - appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + sendNotification(notification, student) } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt index cc7e4656..b38e4f60 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt @@ -1,49 +1,33 @@ package io.github.wulkanowy.services.sync.notifications import android.content.Context -import androidx.core.text.parseAsHtml +import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.SchoolAnnouncement import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.GroupNotificationData -import io.github.wulkanowy.data.pojos.NotificationData -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.utils.getPlural +import io.github.wulkanowy.data.pojos.MultipleNotifications +import io.github.wulkanowy.ui.modules.main.MainView import javax.inject.Inject class NewSchoolAnnouncementNotification @Inject constructor( - private val appNotificationManager: AppNotificationManager, - @ApplicationContext private val context: Context -) { + @ApplicationContext private val context: Context, + notificationManager: NotificationManagerCompat, +) : BaseNotification(context, notificationManager) { - suspend fun notify(items: List, student: Student) { - val notificationDataList = items.map { - NotificationData( - destination = Destination.SchoolAnnouncement, - title = context.getPlural( - R.plurals.school_announcement_notify_new_item_title, - 1 - ), - content = "${it.subject}: ${it.content.parseAsHtml()}" - ) - } - val groupNotificationData = GroupNotificationData( + fun notify(items: List, student: Student) { + val notification = MultipleNotifications( type = NotificationType.NEW_ANNOUNCEMENT, - destination = Destination.SchoolAnnouncement, - title = context.getPlural( - R.plurals.school_announcement_notify_new_item_title, - items.size - ), - content = context.getPlural( - R.plurals.school_announcement_notify_new_items, - items.size, - items.size - ), - notificationDataList = notificationDataList + icon = R.drawable.ic_all_about, + titleStringRes = R.plurals.school_announcement_notify_new_item_title, + contentStringRes = R.plurals.school_announcement_notify_new_items, + summaryStringRes = R.plurals.school_announcement_number_item, + startMenu = MainView.Section.SCHOOL_ANNOUNCEMENT, + lines = items.map { + "${it.subject}: ${it.content}" + } ) - appNotificationManager.sendMultipleNotifications(groupNotificationData, student) + sendNotification(notification, student) } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NotificationType.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NotificationType.kt index 023ae2e4..c3df1960 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NotificationType.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NotificationType.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.services.sync.notifications -import io.github.wulkanowy.R import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel -import io.github.wulkanowy.services.sync.channels.NewAttendanceChannel import io.github.wulkanowy.services.sync.channels.NewConferencesChannel import io.github.wulkanowy.services.sync.channels.NewExamChannel import io.github.wulkanowy.services.sync.channels.NewGradesChannel @@ -10,63 +8,16 @@ import io.github.wulkanowy.services.sync.channels.NewHomeworkChannel import io.github.wulkanowy.services.sync.channels.NewMessagesChannel import io.github.wulkanowy.services.sync.channels.NewNotesChannel import io.github.wulkanowy.services.sync.channels.NewSchoolAnnouncementsChannel -import io.github.wulkanowy.services.sync.channels.PushChannel -import io.github.wulkanowy.services.sync.channels.TimetableChangeChannel -enum class NotificationType( - val channel: String, - val icon: Int -) { - NEW_CONFERENCE( - channel = NewConferencesChannel.CHANNEL_ID, - icon = R.drawable.ic_more_conferences, - ), - NEW_EXAM( - channel = NewExamChannel.CHANNEL_ID, - icon = R.drawable.ic_main_exam - ), - NEW_GRADE_DETAILS( - channel = NewGradesChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_grade, - ), - NEW_GRADE_PREDICTED( - channel = NewGradesChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_grade, - ), - NEW_GRADE_FINAL( - channel = NewGradesChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_grade, - ), - NEW_HOMEWORK( - channel = NewHomeworkChannel.CHANNEL_ID, - icon = R.drawable.ic_more_homework, - ), - NEW_LUCKY_NUMBER( - channel = LuckyNumberChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_luckynumber, - ), - NEW_MESSAGE( - channel = NewMessagesChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_message, - ), - NEW_NOTE( - channel = NewNotesChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_note - ), - NEW_ANNOUNCEMENT( - channel = NewSchoolAnnouncementsChannel.CHANNEL_ID, - icon = R.drawable.ic_all_about - ), - CHANGE_TIMETABLE( - channel = TimetableChangeChannel.CHANNEL_ID, - icon = R.drawable.ic_main_timetable - ), - NEW_ATTENDANCE( - channel = NewAttendanceChannel.CHANNEL_ID, - icon = R.drawable.ic_main_attendance - ), - PUSH( - channel = PushChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_all - ) +enum class NotificationType(val group: String, val channel: String) { + NEW_CONFERENCE("new_conferences_group", NewConferencesChannel.CHANNEL_ID), + NEW_EXAM("new_exam_group", NewExamChannel.CHANNEL_ID), + NEW_GRADE_DETAILS("new_grade_details_group", NewGradesChannel.CHANNEL_ID), + NEW_GRADE_PREDICTED("new_grade_predicted_group", NewGradesChannel.CHANNEL_ID), + NEW_GRADE_FINAL("new_grade_final_group", NewGradesChannel.CHANNEL_ID), + NEW_HOMEWORK("new_homework_group", NewHomeworkChannel.CHANNEL_ID), + NEW_LUCKY_NUMBER("lucky_number_group", LuckyNumberChannel.CHANNEL_ID), + NEW_MESSAGE("new_message_group", NewMessagesChannel.CHANNEL_ID), + NEW_NOTE("new_notes_group", NewNotesChannel.CHANNEL_ID), + NEW_ANNOUNCEMENT("new_school_announcements_group", NewSchoolAnnouncementsChannel.CHANNEL_ID), } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt index 55ce7e90..cbe1fe6b 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt @@ -3,19 +3,14 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository -import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.utils.waitForResult import javax.inject.Inject class AttendanceSummaryWork @Inject constructor( private val attendanceSummaryRepository: AttendanceSummaryRepository ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { - attendanceSummaryRepository.getAttendanceSummary( - student = student, - semester = semester, - subjectId = -1, - forceRefresh = true, - ).waitForResult() + override suspend fun doWork(student: Student, semester: Semester) { + attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, true).waitForResult() } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt index 657f6963..788e4ea2 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt @@ -3,38 +3,15 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.AttendanceRepository -import io.github.wulkanowy.data.waitForResult -import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification -import io.github.wulkanowy.utils.previousOrSameSchoolDay -import kotlinx.coroutines.flow.first +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.waitForResult import java.time.LocalDate.now import javax.inject.Inject -class AttendanceWork @Inject constructor( - private val attendanceRepository: AttendanceRepository, - private val newAttendanceNotification: NewAttendanceNotification, -) : Work { +class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { - attendanceRepository.getAttendance( - student = student, - semester = semester, - start = now().previousOrSameSchoolDay, - end = now().previousOrSameSchoolDay, - forceRefresh = true, - notify = notify, - ) - .waitForResult() - - attendanceRepository.getAttendanceFromDatabase(semester, now().minusDays(7), now()) - .first() - .filterNot { it.isNotified } - .let { - if (it.isNotEmpty()) newAttendanceNotification.notify(it, student) - - attendanceRepository.updateTimetable(it.onEach { attendance -> - attendance.isNotified = true - }) - } + override suspend fun doWork(student: Student, semester: Semester) { + attendanceRepository.getAttendance(student, semester, now().monday, now().sunday, true).waitForResult() } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt index f898aa04..17bd6129 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt @@ -3,9 +3,9 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.CompletedLessonsRepository -import io.github.wulkanowy.data.waitForResult import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.waitForResult import java.time.LocalDate.now import javax.inject.Inject @@ -13,13 +13,7 @@ class CompletedLessonWork @Inject constructor( private val completedLessonsRepository: CompletedLessonsRepository ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { - completedLessonsRepository.getCompletedLessons( - student = student, - semester = semester, - start = now().monday, - end = now().sunday, - forceRefresh = true, - ).waitForResult() + override suspend fun doWork(student: Student, semester: Semester) { + completedLessonsRepository.getCompletedLessons(student, semester, now().monday, now().sunday, true).waitForResult() } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt index c85c0043..002b4f76 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt @@ -3,22 +3,24 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.ConferenceRepository -import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.sync.notifications.NewConferenceNotification +import io.github.wulkanowy.utils.waitForResult import kotlinx.coroutines.flow.first import javax.inject.Inject class ConferenceWork @Inject constructor( private val conferenceRepository: ConferenceRepository, + private val preferencesRepository: PreferencesRepository, private val newConferenceNotification: NewConferenceNotification, ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + override suspend fun doWork(student: Student, semester: Semester) { conferenceRepository.getConferences( student = student, semester = semester, forceRefresh = true, - notify = notify + notify = preferencesRepository.isNotificationsEnable ).waitForResult() conferenceRepository.getConferenceFromDatabase(semester).first() diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt index 7071bce2..a1ce553a 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt @@ -3,25 +3,27 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.ExamRepository -import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.sync.notifications.NewExamNotification +import io.github.wulkanowy.utils.waitForResult import kotlinx.coroutines.flow.first import java.time.LocalDate.now import javax.inject.Inject class ExamWork @Inject constructor( private val examRepository: ExamRepository, + private val preferencesRepository: PreferencesRepository, private val newExamNotification: NewExamNotification, ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + override suspend fun doWork(student: Student, semester: Semester) { examRepository.getExams( student = student, semester = semester, start = now(), end = now(), forceRefresh = true, - notify = notify, + notify = preferencesRepository.isNotificationsEnable ).waitForResult() examRepository.getExamsFromDatabase(semester, now()).first() diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt index ac35bc9a..4575b419 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt @@ -3,15 +3,14 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.GradeStatisticsRepository -import io.github.wulkanowy.data.waitForResult - +import io.github.wulkanowy.utils.waitForResult import javax.inject.Inject class GradeStatisticsWork @Inject constructor( private val gradeStatisticsRepository: GradeStatisticsRepository ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + override suspend fun doWork(student: Student, semester: Semester) { with(gradeStatisticsRepository) { getGradesPartialStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult() getGradesSemesterStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult() diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt index ba21b860..0932405e 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt @@ -3,22 +3,24 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.GradeRepository -import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.sync.notifications.NewGradeNotification +import io.github.wulkanowy.utils.waitForResult import kotlinx.coroutines.flow.first import javax.inject.Inject class GradeWork @Inject constructor( private val gradeRepository: GradeRepository, + private val preferencesRepository: PreferencesRepository, private val newGradeNotification: NewGradeNotification, ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + override suspend fun doWork(student: Student, semester: Semester) { gradeRepository.getGrades( student = student, semester = semester, forceRefresh = true, - notify = notify, + notify = preferencesRepository.isNotificationsEnable ).waitForResult() gradeRepository.getGradesFromDatabase(semester).first() diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt index 4cfe27d0..da2dcc7f 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt @@ -3,29 +3,32 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.HomeworkRepository -import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.sync.notifications.NewHomeworkNotification -import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.waitForResult import kotlinx.coroutines.flow.first import java.time.LocalDate.now import javax.inject.Inject class HomeworkWork @Inject constructor( private val homeworkRepository: HomeworkRepository, + private val preferencesRepository: PreferencesRepository, private val newHomeworkNotification: NewHomeworkNotification, ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + override suspend fun doWork(student: Student, semester: Semester) { homeworkRepository.getHomework( student = student, semester = semester, - start = now().nextOrSameSchoolDay, - end = now().nextOrSameSchoolDay, + start = now().monday, + end = now().sunday, forceRefresh = true, - notify = notify, + notify = preferencesRepository.isNotificationsEnable ).waitForResult() - homeworkRepository.getHomeworkFromDatabase(semester, now(), now().plusDays(7)).first() + homeworkRepository.getHomeworkFromDatabase(semester, now().monday, now().sunday).first() .filter { !it.isNotified }.let { if (it.isNotEmpty()) newHomeworkNotification.notify(it, student) diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt index 668b1b6b..348f9214 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt @@ -3,20 +3,22 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.LuckyNumberRepository -import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.sync.notifications.NewLuckyNumberNotification +import io.github.wulkanowy.utils.waitForResult import javax.inject.Inject class LuckyNumberWork @Inject constructor( private val luckyNumberRepository: LuckyNumberRepository, + private val preferencesRepository: PreferencesRepository, private val newLuckyNumberNotification: NewLuckyNumberNotification, ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + override suspend fun doWork(student: Student, semester: Semester) { luckyNumberRepository.getLuckyNumber( student = student, forceRefresh = true, - notify = notify, + notify = preferencesRepository.isNotificationsEnable ).waitForResult() luckyNumberRepository.getNotNotifiedLuckyNumber(student)?.let { diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt index c7824e61..b5624a76 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt @@ -4,27 +4,28 @@ import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED import io.github.wulkanowy.data.repositories.MessageRepository -import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.sync.notifications.NewMessageNotification +import io.github.wulkanowy.utils.waitForResult import kotlinx.coroutines.flow.first import javax.inject.Inject class MessageWork @Inject constructor( private val messageRepository: MessageRepository, + private val preferencesRepository: PreferencesRepository, private val newMessageNotification: NewMessageNotification, ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { - val mailbox = messageRepository.getMailboxByStudent(student) + override suspend fun doWork(student: Student, semester: Semester) { messageRepository.getMessages( student = student, - mailbox = mailbox, + semester = semester, folder = RECEIVED, forceRefresh = true, - notify = notify + notify = preferencesRepository.isNotificationsEnable ).waitForResult() - messageRepository.getMessagesFromDatabase(student, mailbox).first() + messageRepository.getMessagesFromDatabase(student).first() .filter { !it.isNotified && it.unread }.let { if (it.isNotEmpty()) newMessageNotification.notify(it, student) messageRepository.updateMessages(it.onEach { message -> message.isNotified = true }) diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt index df6e2b06..6f18eddf 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt @@ -3,22 +3,24 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.NoteRepository -import io.github.wulkanowy.data.waitForResult +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.sync.notifications.NewNoteNotification +import io.github.wulkanowy.utils.waitForResult import kotlinx.coroutines.flow.first import javax.inject.Inject class NoteWork @Inject constructor( private val noteRepository: NoteRepository, + private val preferencesRepository: PreferencesRepository, private val newNoteNotification: NewNoteNotification, ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + override suspend fun doWork(student: Student, semester: Semester) { noteRepository.getNotes( student = student, semester = semester, forceRefresh = true, - notify = notify, + notify = preferencesRepository.isNotificationsEnable ).waitForResult() noteRepository.getNotesFromDatabase(student).first() diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt index 90b20651..34ab3db0 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt @@ -1,23 +1,23 @@ package io.github.wulkanowy.services.sync.works -import io.github.wulkanowy.data.dataOrNull -import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.repositories.RecipientRepository -import io.github.wulkanowy.data.toFirstResult +import io.github.wulkanowy.data.repositories.ReportingUnitRepository import javax.inject.Inject class RecipientWork @Inject constructor( - private val messageRepository: MessageRepository, + private val reportingUnitRepository: ReportingUnitRepository, private val recipientRepository: RecipientRepository ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { - val mailboxes = messageRepository.getMailboxes(student, forceRefresh = true).toFirstResult() - mailboxes.dataOrNull?.forEach { - recipientRepository.refreshRecipients(student, it, MailboxType.EMPLOYEE) + override suspend fun doWork(student: Student, semester: Semester) { + reportingUnitRepository.refreshReportingUnits(student) + + reportingUnitRepository.getReportingUnits(student).let { units -> + units.map { + recipientRepository.refreshRecipients(student, it, 2) + } } } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt index 1aedc839..268992f4 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt @@ -2,32 +2,30 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository -import io.github.wulkanowy.data.waitForResult import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification +import io.github.wulkanowy.utils.waitForResult import kotlinx.coroutines.flow.first -import java.time.LocalDate import javax.inject.Inject class SchoolAnnouncementWork @Inject constructor( private val schoolAnnouncementRepository: SchoolAnnouncementRepository, + private val preferencesRepository: PreferencesRepository, private val newSchoolAnnouncementNotification: NewSchoolAnnouncementNotification, ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + override suspend fun doWork(student: Student, semester: Semester) { schoolAnnouncementRepository.getSchoolAnnouncements( student = student, forceRefresh = true, - notify = notify, + notify = preferencesRepository.isNotificationsEnable ).waitForResult() - schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student) - .first() - .filter { !it.isNotified && it.date >= LocalDate.now() } - .let { - if (it.isNotEmpty()) { - newSchoolAnnouncementNotification.notify(it, student) - } + + schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student).first() + .filter { !it.isNotified }.let { + if (it.isNotEmpty()) newSchoolAnnouncementNotification.notify(it, student) schoolAnnouncementRepository.updateSchoolAnnouncement(it.onEach { schoolAnnouncement -> schoolAnnouncement.isNotified = true diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt index e7c72bf0..7c614c6c 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt @@ -3,13 +3,12 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.TeacherRepository -import io.github.wulkanowy.data.waitForResult - +import io.github.wulkanowy.utils.waitForResult import javax.inject.Inject class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { + override suspend fun doWork(student: Student, semester: Semester) { teacherRepository.getTeachers(student, semester, true).waitForResult() } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt index 29b1f13c..2df2c9dc 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt @@ -3,38 +3,17 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.TimetableRepository -import io.github.wulkanowy.data.waitForResult -import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification -import io.github.wulkanowy.utils.nextOrSameSchoolDay -import kotlinx.coroutines.flow.first +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.waitForResult import java.time.LocalDate.now import javax.inject.Inject class TimetableWork @Inject constructor( - private val timetableRepository: TimetableRepository, - private val changeTimetableNotification: ChangeTimetableNotification, + private val timetableRepository: TimetableRepository ) : Work { - override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { - timetableRepository.getTimetable( - student = student, - semester = semester, - start = now().nextOrSameSchoolDay, - end = now().nextOrSameSchoolDay, - forceRefresh = true, - notify = notify, - ) - .waitForResult() - - timetableRepository.getTimetableFromDatabase(semester, now(), now().plusDays(7)) - .first() - .filterNot { it.isNotified } - .let { - if (it.isNotEmpty()) changeTimetableNotification.notify(it, student) - - timetableRepository.updateTimetable(it.onEach { timetable -> - timetable.isNotified = true - }) - } + override suspend fun doWork(student: Student, semester: Semester) { + timetableRepository.getTimetable(student, semester, now().monday, now().sunday, true).waitForResult() } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt index 1c0214cd..c41f41ce 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt @@ -5,5 +5,5 @@ import io.github.wulkanowy.data.db.entities.Student interface Work { - suspend fun doWork(student: Student, semester: Semester, notify: Boolean) + suspend fun doWork(student: Student, semester: Semester) } diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt index d48556fa..45cd2b04 100644 --- a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt +++ b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt @@ -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 @@ -23,13 +24,14 @@ class TimetableWidgetService : RemoteViewsService() { @Inject lateinit var semesterRepo: SemesterRepository + @Inject + lateinit var prefRepository: PreferencesRepository + @Inject lateinit var sharedPref: SharedPrefProvider override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory { Timber.d("TimetableWidgetFactory created") - return TimetableWidgetFactory( - timetableRepo, studentRepo, semesterRepo, sharedPref, applicationContext, intent - ) + return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, prefRepository, sharedPref, applicationContext, intent) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt index f622209a..0521b4a0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt @@ -1,16 +1,18 @@ package io.github.wulkanowy.ui.base import android.app.ActivityManager +import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.os.Bundle import android.view.View import android.widget.Toast +import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.app.AppCompatDelegate import androidx.viewbinding.ViewBinding -import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar.LENGTH_LONG import io.github.wulkanowy.R -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.utils.FragmentLifecycleLogger import io.github.wulkanowy.utils.getThemeAttrColor @@ -31,8 +33,6 @@ abstract class BaseActivity, VB : ViewBinding> : protected var messageContainer: View? = null - protected var messageAnchor: View? = null - abstract var presenter: T override fun onCreate(savedInstanceState: Bundle?) { @@ -40,6 +40,7 @@ abstract class BaseActivity, VB : ViewBinding> : themeManager.applyActivityTheme(this) super.onCreate(savedInstanceState) supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true) + AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) @Suppress("DEPRECATION") setTaskDescription( @@ -51,7 +52,6 @@ abstract class BaseActivity, VB : ViewBinding> : if (messageContainer != null) { Snackbar.make(messageContainer!!, text, LENGTH_LONG) .setAction(R.string.all_details) { showErrorDetailsDialog(error) } - .apply { messageAnchor?.let { anchorView = it } } .show() } else showMessage(text) } @@ -61,15 +61,12 @@ abstract class BaseActivity, VB : ViewBinding> : } override fun showMessage(text: String) { - if (messageContainer != null) { - Snackbar.make(messageContainer!!, text, LENGTH_LONG) - .apply { messageAnchor?.let { anchorView = it } } - .show() - } else Toast.makeText(this, text, Toast.LENGTH_LONG).show() + if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show() + else Toast.makeText(this, text, Toast.LENGTH_LONG).show() } override fun showExpiredDialog() { - MaterialAlertDialogBuilder(this) + AlertDialog.Builder(this) .setTitle(R.string.main_session_expired) .setMessage(R.string.main_session_relogin) .setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onExpiredLoginSelected() } @@ -77,22 +74,17 @@ abstract class BaseActivity, VB : ViewBinding> : .show() } - override fun showAuthDialog() { - AuthDialog.newInstance().show(supportFragmentManager, "auth_dialog") - } - override fun showChangePasswordSnackbar(redirectUrl: String) { messageContainer?.let { Snackbar.make(it, R.string.error_password_change_required, LENGTH_LONG) .setAction(R.string.all_change) { openInternetBrowser(redirectUrl) } - .apply { messageAnchor?.let { anchorView = it } } .show() } } override fun openClearLoginView() { - startActivity(LoginActivity.getStartIntent(this)) - finishAffinity() + startActivity(LoginActivity.getStartIntent(this) + .apply { addFlags(FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK) }) } override fun onDestroy() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt index 84540b1c..25a53395 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt @@ -1,14 +1,8 @@ package io.github.wulkanowy.ui.base -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup import android.widget.Toast import androidx.fragment.app.DialogFragment import androidx.viewbinding.ViewBinding -import com.google.android.material.elevation.SurfaceColors -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.lifecycleAwareVariable import javax.inject.Inject @@ -40,25 +34,10 @@ abstract class BaseDialogFragment : DialogFragment(), BaseView (activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl) } - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") - } - override fun showErrorDetailsDialog(error: Throwable) { ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - view.setBackgroundColor(SurfaceColors.SURFACE_3.getColor(requireContext())) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = binding.root - override fun onResume() { super.onResume() analyticsHelper.setCurrentScreen(requireActivity(), this::class.simpleName) diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt index b25346a7..dbc5af3a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt @@ -7,7 +7,6 @@ import androidx.viewbinding.ViewBinding import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar.LENGTH_LONG import io.github.wulkanowy.R -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.utils.lifecycleAwareVariable abstract class BaseFragment(@LayoutRes layoutId: Int) : Fragment(layoutId), @@ -43,10 +42,6 @@ abstract class BaseFragment(@LayoutRes layoutId: Int) : Fragme (activity as? BaseActivity<*, *>)?.showExpiredDialog() } - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") - } - override fun openClearLoginView() { (activity as? BaseActivity<*, *>)?.openClearLoginView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt index 6bca87f1..bd735535 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt @@ -2,33 +2,32 @@ package io.github.wulkanowy.ui.base import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager -import androidx.lifecycle.Lifecycle -import androidx.viewpager2.adapter.FragmentStateAdapter -import com.google.android.material.tabs.TabLayout -import com.google.android.material.tabs.TabLayoutMediator +import androidx.fragment.app.FragmentPagerAdapter -class BaseFragmentPagerAdapter( - private val fragmentManager: FragmentManager, - private val pagesCount: Int, - lifecycle: Lifecycle, -) : FragmentStateAdapter(fragmentManager, lifecycle), TabLayoutMediator.TabConfigurationStrategy { +//TODO Use ViewPager2 +class BaseFragmentPagerAdapter(private val fragmentManager: FragmentManager) : + FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { - lateinit var itemFactory: (position: Int) -> Fragment - - var titleFactory: (position: Int) -> String? = { "" } + private val pages = mutableMapOf() var containerId = 0 fun getFragmentInstance(position: Int): Fragment? { require(containerId != 0) { "Container id is 0" } - return fragmentManager.findFragmentByTag("f$position") + return fragmentManager.findFragmentByTag("android:switcher:$containerId:$position") } - override fun createFragment(position: Int): Fragment = itemFactory(position) - - override fun getItemCount() = pagesCount - - override fun onConfigureTab(tab: TabLayout.Tab, position: Int) { - tab.text = titleFactory(position) + fun addFragments(fragments: List) { + fragments.forEach { pages[it] = null } } + + fun addFragmentsWithTitle(pages: Map) { + this.pages.putAll(pages) + } + + override fun getItem(position: Int) = pages.keys.elementAt(position) + + override fun getCount() = pages.size + + override fun getPageTitle(position: Int) = pages.values.elementAt(position) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt index 2d913103..be530049 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt @@ -1,82 +1,85 @@ package io.github.wulkanowy.ui.base +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.utils.flowWithResource import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.onEach import timber.log.Timber +import kotlin.coroutines.CoroutineContext open class BasePresenter( protected val errorHandler: ErrorHandler, protected val studentRepository: StudentRepository -) { - private val job = SupervisorJob() +) : CoroutineScope { - protected val presenterScope = CoroutineScope(job + Dispatchers.Main) + private var job: Job = Job() - private val childrenJobs = mutableMapOf() + private val jobs = mutableMapOf() + + override val coroutineContext: CoroutineContext + get() = Dispatchers.Main + job var view: T? = null open fun onAttachView(view: T) { + job = Job() this.view = view errorHandler.apply { showErrorMessage = view::showError onSessionExpired = view::showExpiredDialog onNoCurrentStudent = view::openClearLoginView onPasswordChangeRequired = view::showChangePasswordSnackbar - onAuthorizationRequired = view::showAuthDialog } } fun onExpiredLoginSelected() { - Timber.i("Attempt to switch the student after the session expires") + flowWithResource { + val student = studentRepository.getCurrentStudent(false) + studentRepository.logoutStudent(student) - presenterScope.launch { - runCatching { - val student = studentRepository.getCurrentStudent(false) - studentRepository.logoutStudent(student) - - val students = studentRepository.getSavedStudents(false) - if (students.isNotEmpty()) { - Timber.i("Switching current student") - studentRepository.switchStudent(students[0]) - } + val students = studentRepository.getSavedStudents(false) + if (students.isNotEmpty()) { + Timber.i("Switching current student") + studentRepository.switchStudent(students[0]) } - .onFailure { - Timber.i("Switch student result: An exception occurred") - errorHandler.dispatch(it) - } - .onSuccess { + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Attempt to switch the student after the session expires") + Status.SUCCESS -> { Timber.i("Switch student result: Open login view") view?.openClearLoginView() } - } + Status.ERROR -> { + Timber.i("Switch student result: An exception occurred") + errorHandler.dispatch(it.error!!) + } + } + }.launch("expired") } fun Flow.launch(individualJobTag: String = "load"): Job { - childrenJobs[individualJobTag]?.cancel() - val job = catch { errorHandler.dispatch(it) }.launchIn(presenterScope) - childrenJobs[individualJobTag] = job + jobs[individualJobTag]?.cancel() + val job = catch { errorHandler.dispatch(it) }.launchIn(this@BasePresenter) + jobs[individualJobTag] = job Timber.d("Job $individualJobTag launched in ${this@BasePresenter.javaClass.simpleName}: $job") return job } fun cancelJobs(vararg names: String) { names.forEach { - childrenJobs[it]?.cancel() + jobs[it]?.cancel() } } open fun onDetachView() { - job.cancelChildren() - errorHandler.clear() view = null + job.cancel() + errorHandler.clear() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt index b31737e2..d3165ea4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt @@ -8,8 +8,6 @@ interface BaseView { fun showExpiredDialog() - fun showAuthDialog() - fun openClearLoginView() fun showErrorDetailsDialog(error: Throwable) diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt index 679d904a..4ce97770 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt @@ -1,84 +1,105 @@ package io.github.wulkanowy.ui.base -import android.app.Dialog import android.content.ClipData import android.content.ClipboardManager import android.os.Bundle +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup +import android.widget.HorizontalScrollView import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.appcompat.app.AlertDialog import androidx.core.content.getSystemService -import androidx.core.os.bundleOf -import androidx.core.view.isGone -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.DialogErrorBinding -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException +import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException +import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.getString +import io.github.wulkanowy.utils.openAppInMarket +import io.github.wulkanowy.utils.openEmailClient +import io.github.wulkanowy.utils.openInternetBrowser +import okhttp3.internal.http2.StreamResetException +import java.io.InterruptedIOException +import java.io.PrintWriter +import java.io.StringWriter +import java.net.ConnectException +import java.net.SocketTimeoutException +import java.net.UnknownHostException import javax.inject.Inject @AndroidEntryPoint class ErrorDialog : BaseDialogFragment() { + private lateinit var error: Throwable + @Inject lateinit var appInfo: AppInfo - @Inject - lateinit var preferencesRepository: PreferencesRepository - - private lateinit var error: Throwable - companion object { - private const val ARGUMENT_KEY = "error" + private const val ARGUMENT_KEY = "Data" fun newInstance(error: Throwable) = ErrorDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to error) + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, error) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - error = requireArguments().serializable(ARGUMENT_KEY) - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext()).apply { - val errorStacktrace = error.stackTraceToString() - setTitle(R.string.all_details) - setView(DialogErrorBinding.inflate(layoutInflater).apply { binding = this }.root) - setNeutralButton(R.string.about_feedback) { _, _ -> - openConfirmDialog { openEmailClient(errorStacktrace) } - } - setNegativeButton(android.R.string.cancel) { _, _ -> } - setPositiveButton(android.R.string.copy) { _, _ -> copyErrorToClipboard(errorStacktrace) } - }.create().apply { - setOnShowListener { - getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported() - } + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + error = getSerializable(ARGUMENT_KEY) as Throwable } } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogErrorBinding.inflate(inflater).apply { binding = this }.root + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + val stringWriter = StringWriter().apply { + error.printStackTrace(PrintWriter(this)) + } + with(binding) { - errorDialogHumanizedMessage.text = resources.getErrorString(error) - errorDialogErrorMessage.text = error.localizedMessage - errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank() - errorDialogContent.text = error.stackTraceToString() - .replace(": ${error.localizedMessage}", "") + errorDialogContent.text = stringWriter.toString() + with(errorDialogHorizontalScroll) { + post { fullScroll(HorizontalScrollView.FOCUS_LEFT) } + } + errorDialogCopy.setOnClickListener { + val clip = ClipData.newPlainText("wulkanowy", stringWriter.toString()) + activity?.getSystemService()?.setPrimaryClip(clip) + + Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show() + } + errorDialogCancel.setOnClickListener { dismiss() } + errorDialogReport.setOnClickListener { + openConfirmDialog { openEmailClient(stringWriter.toString()) } + } + errorDialogMessage.text = resources.getString(error) + errorDialogReport.isEnabled = when (error) { + is UnknownHostException, + is InterruptedIOException, + is ConnectException, + is StreamResetException, + is SocketTimeoutException, + is ServiceUnavailableException, + is FeatureDisabledException, + is FeatureNotAvailableException -> false + else -> true + } } } - private fun copyErrorToClipboard(errorStacktrace: String) { - val clip = ClipData.newPlainText("Error details", errorStacktrace) - requireActivity().getSystemService()?.setPrimaryClip(clip) - Toast.makeText(requireContext(), R.string.all_copied, LENGTH_LONG).show() - } - private fun openConfirmDialog(callback: () -> Unit) { - MaterialAlertDialogBuilder(requireContext()) + AlertDialog.Builder(requireContext()) .setTitle(R.string.dialog_error_check_update) .setMessage(R.string.dialog_error_check_update_message) .setNeutralButton(R.string.about_feedback) { _, _ -> callback() } @@ -97,8 +118,7 @@ class ErrorDialog : BaseDialogFragment() { R.string.about_feedback_template, "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), - "${appInfo.versionName}-${appInfo.buildFlavor}", - preferencesRepository.installationId, + "${appInfo.versionName}-${appInfo.buildFlavor}" ) + "\n" + content, onActivityNotFound = { requireContext().openInternetBrowser( diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt index 0a41a47b..7c32ef18 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt @@ -1,17 +1,15 @@ package io.github.wulkanowy.ui.base -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext +import android.content.res.Resources import io.github.wulkanowy.data.exceptions.NoCurrentStudentException -import io.github.wulkanowy.sdk.scrapper.exception.AuthorizationRequiredException import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException -import io.github.wulkanowy.utils.getErrorString +import io.github.wulkanowy.utils.getString import io.github.wulkanowy.utils.security.ScramblerException import timber.log.Timber import javax.inject.Inject -open class ErrorHandler @Inject constructor(@ApplicationContext protected val context: Context) { +open class ErrorHandler @Inject constructor(protected val resources: Resources) { var showErrorMessage: (String, Throwable) -> Unit = { _, _ -> } @@ -21,20 +19,17 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co var onPasswordChangeRequired: (String) -> Unit = {} - var onAuthorizationRequired: () -> Unit = {} - fun dispatch(error: Throwable) { Timber.e(error, "An exception occurred while the Wulkanowy was running") proceed(error) } protected open fun proceed(error: Throwable) { - showErrorMessage(context.resources.getErrorString(error), error) + showErrorMessage(resources.getString(error), error) when (error) { is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl) is ScramblerException, is BadCredentialsException -> onSessionExpired() is NoCurrentStudentException -> onNoCurrentStudent() - is AuthorizationRequiredException -> onAuthorizationRequired() } } @@ -43,6 +38,5 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co onSessionExpired = {} onNoCurrentStudent = {} onPasswordChangeRequired = {} - onAuthorizationRequired = {} } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt index f42f315c..b560ed2e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt @@ -1,19 +1,16 @@ package io.github.wulkanowy.ui.base -import android.content.pm.PackageInfo -import android.content.pm.PackageManager import android.content.pm.PackageManager.GET_ACTIVITIES -import android.os.Build import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate -import com.google.android.material.color.DynamicColors +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES import io.github.wulkanowy.R -import io.github.wulkanowy.data.enums.AppTheme import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity +import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import javax.inject.Inject import javax.inject.Singleton @@ -23,44 +20,35 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer fun applyActivityTheme(activity: AppCompatActivity) { if (isThemeApplicable(activity)) { applyDefaultTheme() - if (preferencesRepository.appTheme == AppTheme.BLACK) { + if (preferencesRepository.appTheme == "black") { when (activity) { is MainActivity -> activity.setTheme(R.style.WulkanowyTheme_Black) is LoginActivity -> activity.setTheme(R.style.WulkanowyTheme_Login_Black) + is SendMessageActivity -> activity.setTheme(R.style.WulkanowyTheme_MessageSend_Black) } } - } else if (activity is TimetableWidgetConfigureActivity || activity is LuckyNumberWidgetConfigureActivity) { - DynamicColors.applyToActivityIfAvailable(activity) } } fun applyDefaultTheme() { AppCompatDelegate.setDefaultNightMode( - when (preferencesRepository.appTheme) { - AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO - AppTheme.DARK, AppTheme.BLACK -> AppCompatDelegate.MODE_NIGHT_YES - AppTheme.SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + when (val theme = preferencesRepository.appTheme) { + "light" -> MODE_NIGHT_NO + "dark", "black" -> MODE_NIGHT_YES + "system" -> MODE_NIGHT_FOLLOW_SYSTEM + else -> throw IllegalArgumentException("Wrong theme: $theme") } ) } - private fun isThemeApplicable(activity: AppCompatActivity): Boolean = - getPackageInfo(activity) - .activities - .singleOrNull { it.name == activity::class.java.canonicalName } - ?.theme - .let { + private fun isThemeApplicable(activity: AppCompatActivity): Boolean { + return activity.packageManager + .getPackageInfo(activity.packageName, GET_ACTIVITIES) + .activities.singleOrNull { it.name == activity::class.java.canonicalName } + ?.theme.let { it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar || it == R.style.WulkanowyTheme_Login || it == R.style.WulkanowyTheme_Login_Black + || it == R.style.WulkanowyTheme_MessageSend || it == R.style.WulkanowyTheme_MessageSend_Black } - - @Suppress("DEPRECATION") - private fun getPackageInfo(activity: AppCompatActivity): PackageInfo { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - activity.packageManager.getPackageInfo( - activity.packageName, - PackageManager.PackageInfoFlags.of(GET_ACTIVITIES.toLong()) - ) - } else activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt deleted file mode 100644 index f0969fac..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt +++ /dev/null @@ -1,152 +0,0 @@ -package io.github.wulkanowy.ui.modules - -import androidx.fragment.app.Fragment -import io.github.wulkanowy.data.serializers.LocalDateSerializer -import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment -import io.github.wulkanowy.ui.modules.conference.ConferenceFragment -import io.github.wulkanowy.ui.modules.dashboard.DashboardFragment -import io.github.wulkanowy.ui.modules.exam.ExamFragment -import io.github.wulkanowy.ui.modules.grade.GradeFragment -import io.github.wulkanowy.ui.modules.homework.HomeworkFragment -import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment -import io.github.wulkanowy.ui.modules.luckynumber.history.LuckyNumberHistoryFragment -import io.github.wulkanowy.ui.modules.message.MessageFragment -import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment -import io.github.wulkanowy.ui.modules.more.MoreFragment -import io.github.wulkanowy.ui.modules.note.NoteFragment -import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment -import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment -import io.github.wulkanowy.ui.modules.settings.SettingsFragment -import io.github.wulkanowy.ui.modules.timetable.TimetableFragment -import kotlinx.serialization.Serializable -import java.time.LocalDate - -@Serializable -sealed class Destination { - - /* - Type in children classes have to be as getter to avoid null in enums - https://stackoverflow.com/questions/68866453/kotlin-enum-val-is-returning-null-despite-being-set-at-compile-time - */ - abstract val destinationType: Type - - abstract val destinationFragment: Fragment - - enum class Type(val defaultDestination: Destination) { - DASHBOARD(Dashboard), - GRADE(Grade), - ATTENDANCE(Attendance), - EXAM(Exam), - TIMETABLE(Timetable()), - HOMEWORK(Homework), - NOTE(Note), - CONFERENCE(Conference), - SCHOOL_ANNOUNCEMENT(SchoolAnnouncement), - SCHOOL_AND_TEACHERS(SchoolAndTeachers), - LUCKY_NUMBER(LuckyNumber), - LUCKY_NUMBER_HISTORY(LuckyNumberHistory), - MORE(More), - MESSAGE(Message), - MOBILE_DEVICE(MobileDevice), - SETTINGS(Settings); - } - - @Serializable - object Dashboard : Destination() { - override val destinationType get() = Type.DASHBOARD - override val destinationFragment get() = DashboardFragment.newInstance() - } - - @Serializable - object Grade : Destination() { - override val destinationType get() = Type.GRADE - override val destinationFragment get() = GradeFragment.newInstance() - } - - @Serializable - object Attendance : Destination() { - override val destinationType get() = Type.ATTENDANCE - override val destinationFragment get() = AttendanceFragment.newInstance() - } - - @Serializable - object Exam : Destination() { - override val destinationType get() = Type.EXAM - override val destinationFragment get() = ExamFragment.newInstance() - } - - @Serializable - data class Timetable( - @Serializable(with = LocalDateSerializer::class) - private val date: LocalDate? = null - ) : Destination() { - override val destinationType get() = Type.TIMETABLE - override val destinationFragment get() = TimetableFragment.newInstance(date) - } - - @Serializable - object Homework : Destination() { - override val destinationType get() = Type.HOMEWORK - override val destinationFragment get() = HomeworkFragment.newInstance() - } - - @Serializable - object Note : Destination() { - override val destinationType get() = Type.NOTE - override val destinationFragment get() = NoteFragment.newInstance() - } - - @Serializable - object Conference : Destination() { - override val destinationType get() = Type.CONFERENCE - override val destinationFragment get() = ConferenceFragment.newInstance() - } - - @Serializable - object SchoolAnnouncement : Destination() { - override val destinationType get() = Type.SCHOOL_ANNOUNCEMENT - override val destinationFragment get() = SchoolAnnouncementFragment.newInstance() - } - - @Serializable - object SchoolAndTeachers : Destination() { - override val destinationType get() = Type.SCHOOL_AND_TEACHERS - override val destinationFragment get() = SchoolAndTeachersFragment.newInstance() - } - - @Serializable - object LuckyNumber : Destination() { - override val destinationType get() = Type.LUCKY_NUMBER - override val destinationFragment get() = LuckyNumberFragment.newInstance() - } - - @Serializable - object LuckyNumberHistory : Destination() { - override val destinationType get() = Type.LUCKY_NUMBER_HISTORY - override val destinationFragment get() = LuckyNumberHistoryFragment.newInstance() - } - - @Serializable - object More : Destination() { - override val destinationType get() = Type.MORE - override val destinationFragment get() = MoreFragment.newInstance() - } - - @Serializable - object Message : Destination() { - override val destinationType get() = Type.MESSAGE - override val destinationFragment get() = MessageFragment.newInstance() - } - - @Serializable - object MobileDevice : Destination() { - override val destinationType get() = Type.MOBILE_DEVICE - override val destinationFragment get() = MobileDeviceFragment.newInstance() - } - - @Serializable - object Settings : Destination() { - override val destinationType get() = Type.SETTINGS - override val destinationFragment get() = SettingsFragment.newInstance() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt index d7f39e30..1bf5c7ad 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt @@ -6,7 +6,6 @@ import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.FragmentAboutBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment @@ -14,8 +13,13 @@ import io.github.wulkanowy.ui.modules.about.license.LicenseFragment import io.github.wulkanowy.ui.modules.debug.DebugFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.* -import java.time.Instant +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.getCompatDrawable +import io.github.wulkanowy.utils.openAppInMarket +import io.github.wulkanowy.utils.openEmailClient +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.toFormattedString +import io.github.wulkanowy.utils.toLocalDateTime import javax.inject.Inject @AndroidEntryPoint @@ -31,13 +35,10 @@ class AboutFragment : BaseFragment(R.layout.fragment_about @Inject lateinit var appInfo: AppInfo - @Inject - lateinit var preferencesRepository: PreferencesRepository - override val versionRes: Triple? get() = context?.run { val buildTimestamp = - Instant.ofEpochMilli(appInfo.buildTimestamp).toFormattedString("yyyy-MM-dd") + appInfo.buildTimestamp.toLocalDateTime().toFormattedString("yyyy-MM-dd") val versionSignature = "${appInfo.versionName}-${appInfo.buildFlavor} (${appInfo.versionCode}), $buildTimestamp" Triple( @@ -189,8 +190,7 @@ class AboutFragment : BaseFragment(R.layout.fragment_about R.string.about_feedback_template, "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), - "${appInfo.versionName}-${appInfo.buildFlavor}", - preferencesRepository.installationId, + "${appInfo.versionName}-${appInfo.buildFlavor}" ), onActivityNotFound = { requireContext().openInternetBrowser( diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt index 55274934..6bcf5f77 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt @@ -82,20 +82,18 @@ class AboutPresenter @Inject constructor( private fun loadData() { view?.run { - updateData( - listOfNotNull( - versionRes, - creatorsRes, - feedbackRes, - faqRes, - discordRes, - facebookRes, - twitterRes, - homepageRes, - licensesRes, - privacyRes - ) - ) + updateData(listOfNotNull( + versionRes, + creatorsRes, + feedbackRes, + faqRes, + discordRes, + facebookRes, + twitterRes, + homepageRes, + licensesRes, + privacyRes + )) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt index 126bb2b4..ef4b540e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt @@ -1,11 +1,13 @@ package io.github.wulkanowy.ui.modules.about.contributor -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.pojos.Contributor import io.github.wulkanowy.data.repositories.AppCreatorRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.flowWithResource +import kotlinx.coroutines.flow.onEach import javax.inject.Inject class ContributorPresenter @Inject constructor( @@ -29,11 +31,15 @@ class ContributorPresenter @Inject constructor( } private fun loadData() { - resourceFlow { appCreatorRepository.getAppCreators() } - .onResourceLoading { view?.showProgress(true) } - .onResourceSuccess { view?.updateData(it) } - .onResourceNotLoading { view?.showProgress(false) } - .onResourceError { errorHandler.dispatch(it) } - .launch() + flowWithResource { appCreatorRepository.getAppCreators() }.onEach { + when (it.status) { + Status.LOADING -> view?.showProgress(true) + Status.SUCCESS -> view?.run { + showProgress(false) + updateData(it.data!!) + } + Status.ERROR -> errorHandler.dispatch(it.error!!) + } + }.launch() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt index adf4ca74..6ae06bbe 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt @@ -23,9 +23,8 @@ class LicenseAdapter @Inject constructor() : RecyclerView.Adapter(R.layout.fragment_l override val titleStringId get() = R.string.license_title - override val appLibraries by lazy { - Libs.Builder().withContext(requireContext()).build().libraries - } + override val appLibraries by lazy { Libs(requireContext()).libraries } companion object { fun newInstance() = LicenseFragment() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt index ddcd5918..cc430fc2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt @@ -1,12 +1,16 @@ package io.github.wulkanowy.ui.modules.about.license import com.mikepenz.aboutlibraries.entity.Library +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.DispatchersProvider -import kotlinx.coroutines.launch +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext +import timber.log.Timber import javax.inject.Inject class LicensePresenter @Inject constructor( @@ -22,20 +26,22 @@ class LicensePresenter @Inject constructor( } fun onItemSelected(library: Library) { - view?.run { library.licenses.firstOrNull()?.licenseContent?.let { openLicense(it) } } + view?.run { library.licenses?.firstOrNull()?.licenseDescription?.let { openLicense(it) } } } private fun loadData() { - presenterScope.launch { - runCatching { - withContext(dispatchers.io) { - view?.appLibraries.orEmpty() - } + flowWithResource { + withContext(dispatchers.backgroundThread) { + view?.appLibraries.orEmpty() } - .onFailure { errorHandler.dispatch(it) } - .onSuccess { view?.updateData(it) } - + }.onEach { + when (it.status) { + Status.LOADING -> Timber.d("License data load started") + Status.SUCCESS -> view?.updateData(it.data!!) + Status.ERROR -> errorHandler.dispatch(it.error!!) + } + }.afterLoading { view?.showProgress(false) - } + }.launch() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt index f115372a..7a8f8585 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt @@ -8,7 +8,7 @@ import androidx.core.view.get import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.FragmentAccountBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment @@ -34,7 +34,6 @@ class AccountFragment : BaseFragment(R.layout.fragment_a override val titleStringId = R.string.account_title - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -76,7 +75,9 @@ class AccountFragment : BaseFragment(R.layout.fragment_a } } - override fun openAccountDetailsView(student: Student) { - (activity as? MainActivity)?.pushView(AccountDetailsFragment.newInstance(student)) + override fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters) { + (activity as? MainActivity)?.pushView( + AccountDetailsFragment.newInstance(studentWithSemesters) + ) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt index 77c1ffe6..8d165139 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt @@ -1,13 +1,12 @@ package io.github.wulkanowy.ui.modules.account +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.onResourceError -import io.github.wulkanowy.data.onResourceSuccess import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.flowWithResource +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -29,14 +28,24 @@ class AccountPresenter @Inject constructor( } fun onItemSelected(studentWithSemesters: StudentWithSemesters) { - view?.openAccountDetailsView(studentWithSemesters.student) + view?.openAccountDetailsView(studentWithSemesters) } private fun loadData() { - resourceFlow { studentRepository.getSavedStudents(false) } - .logResourceStatus("load account data") - .onResourceSuccess { view?.updateData(createAccountItems(it)) } - .onResourceError(errorHandler::dispatch) + flowWithResource { studentRepository.getSavedStudents(false) } + .onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading account data started") + Status.SUCCESS -> { + Timber.i("Loading account result: Success") + view?.updateData(createAccountItems(it.data!!)) + } + Status.ERROR -> { + Timber.i("Loading account result: An exception occurred") + errorHandler.dispatch(it.error!!) + } + } + } .launch("load") } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt index 56fcb0a3..d7deefaf 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.account -import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView interface AccountView : BaseView { @@ -11,5 +11,5 @@ interface AccountView : BaseView { fun openLoginView() - fun openAccountDetailsView(student: Student) + fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt index d6bc6154..f1c7f7bd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt @@ -6,10 +6,8 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import androidx.appcompat.app.AlertDialog -import androidx.core.os.bundleOf import androidx.core.view.get import androidx.core.view.isVisible -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Student @@ -23,7 +21,6 @@ import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView import io.github.wulkanowy.utils.createNameInitialsDrawable import io.github.wulkanowy.utils.nickOrName -import io.github.wulkanowy.utils.serializable import javax.inject.Inject @AndroidEntryPoint @@ -40,12 +37,12 @@ class AccountDetailsFragment : private const val ARGUMENT_KEY = "Data" - fun newInstance(student: Student) = AccountDetailsFragment().apply { - arguments = bundleOf(ARGUMENT_KEY to student) - } + fun newInstance(studentWithSemesters: StudentWithSemesters) = + AccountDetailsFragment().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, studentWithSemesters) } + } } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -54,7 +51,7 @@ class AccountDetailsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentAccountDetailsBinding.bind(view) - presenter.onAttachView(this, requireArguments().serializable(ARGUMENT_KEY)) + presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as StudentWithSemesters) } override fun initView() { @@ -115,7 +112,7 @@ class AccountDetailsFragment : override fun showLogoutConfirmDialog() { context?.let { - MaterialAlertDialogBuilder(it) + AlertDialog.Builder(it) .setTitle(R.string.account_logout_student) .setMessage(R.string.account_confirm) .setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() } @@ -124,14 +121,10 @@ class AccountDetailsFragment : } } - override fun popViewToMain() { + override fun popView() { (requireActivity() as MainActivity).popView(2) } - override fun popViewToAccounts() { - (requireActivity() as MainActivity).popView(1) - } - override fun recreateMainView() { requireActivity().recreate() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt index 5d68ff2e..cc53c9b6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt @@ -1,13 +1,17 @@ package io.github.wulkanowy.ui.modules.account.accountdetails -import io.github.wulkanowy.data.* -import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -23,9 +27,9 @@ class AccountDetailsPresenter @Inject constructor( private var studentId: Long? = null - fun onAttachView(view: AccountDetailsView, student: Student) { + fun onAttachView(view: AccountDetailsView, studentWithSemesters: StudentWithSemesters) { super.onAttachView(view) - studentId = student.id + studentId = studentWithSemesters.student.id view.initView() errorHandler.showErrorMessage = ::showErrorViewOnError @@ -46,25 +50,40 @@ class AccountDetailsPresenter @Inject constructor( } private fun loadData() { - resourceFlow { studentRepository.getSavedStudentById(studentId ?: -1) } - .logResourceStatus("loading account details view") - .onResourceLoading { - view?.run { - showProgress(true) - showContent(false) + flowWithResource { studentRepository.getSavedStudents() } + .map { studentWithSemesters -> + Resource( + data = studentWithSemesters.data?.single { it.student.id == studentId }, + status = studentWithSemesters.status, + error = studentWithSemesters.error + ) + } + .onEach { + when (it.status) { + Status.LOADING -> { + view?.run { + showProgress(true) + showContent(false) + } + Timber.i("Loading account details view started") + } + Status.SUCCESS -> { + Timber.i("Loading account details view result: Success") + studentWithSemesters = it.data + view?.run { + showAccountData(studentWithSemesters!!.student) + enableSelectStudentButton(!studentWithSemesters!!.student.isCurrent) + showContent(true) + showErrorView(false) + } + } + Status.ERROR -> { + Timber.i("Loading account details view result: An exception occurred") + errorHandler.dispatch(it.error!!) + } } } - .onResourceSuccess { - studentWithSemesters = it - view?.run { - showAccountData(studentWithSemesters!!.student) - enableSelectStudentButton(!studentWithSemesters!!.student.isCurrent) - showContent(true) - showErrorView(false) - } - } - .onResourceNotLoading { view?.showProgress(false) } - .onResourceError(errorHandler::dispatch) + .afterLoading { view?.showProgress(false) } .launch() } @@ -85,12 +104,22 @@ class AccountDetailsPresenter @Inject constructor( Timber.i("Select student ${studentWithSemesters!!.student.id}") - resourceFlow { studentRepository.switchStudent(studentWithSemesters!!) } - .logResourceStatus("change student") - .onResourceSuccess { view?.recreateMainView() } - .onResourceNotLoading { view?.popViewToMain() } - .onResourceError(errorHandler::dispatch) - .launch("switch") + flowWithResource { studentRepository.switchStudent(studentWithSemesters!!) } + .onEach { + when (it.status) { + Status.LOADING -> Timber.i("Attempt to change a student") + Status.SUCCESS -> { + Timber.i("Change a student result: Success") + view?.recreateMainView() + } + Status.ERROR -> { + Timber.i("Change a student result: An exception occurred") + errorHandler.dispatch(it.error!!) + } + } + }.afterLoading { + view?.popView() + }.launch("switch") } fun onRemoveSelected() { @@ -101,7 +130,7 @@ class AccountDetailsPresenter @Inject constructor( fun onLogoutConfirm() { if (studentWithSemesters == null) return - resourceFlow { + flowWithResource { val studentToLogout = studentWithSemesters!!.student studentRepository.logoutStudent(studentToLogout) @@ -111,37 +140,32 @@ class AccountDetailsPresenter @Inject constructor( studentRepository.switchStudent(students[0]) } - students - } - .logResourceStatus("logout user") - .onResourceSuccess { - view?.run { + return@flowWithResource students + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Attempt to logout user") + Status.SUCCESS -> view?.run { when { - it.isEmpty() -> { + it.data!!.isEmpty() -> { Timber.i("Logout result: Open login view") syncManager.stopSyncWorker() openClearLoginView() } - studentWithSemesters?.student?.isCurrent == true -> { + studentWithSemesters!!.student.isCurrent -> { Timber.i("Logout result: Logout student and switch to another") recreateMainView() } - else -> { - Timber.i("Logout result: Logout student") - recreateMainView() - } + else -> Timber.i("Logout result: Logout student") } } - } - .onResourceNotLoading { - if (studentWithSemesters?.student?.isCurrent == true) { - view?.popViewToMain() - } else { - view?.popViewToAccounts() + Status.ERROR -> { + Timber.i("Logout result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceError(errorHandler::dispatch) - .launch("logout") + }.afterLoading { + view?.popView() + }.launch("logout") } private fun showErrorViewOnError(message: String, error: Throwable) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsView.kt index aeb743fa..652f0c1a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsView.kt @@ -15,9 +15,7 @@ interface AccountDetailsView : BaseView { fun showLogoutConfirmDialog() - fun popViewToMain() - - fun popViewToAccounts() + fun popView() fun recreateMainView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditColorAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditColorAdapter.kt index 66e39fc7..ab6eec41 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditColorAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditColorAdapter.kt @@ -3,9 +3,12 @@ package io.github.wulkanowy.ui.modules.account.accountedit import android.annotation.SuppressLint import android.content.res.ColorStateList import android.graphics.Color +import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable import android.graphics.drawable.RippleDrawable +import android.graphics.drawable.StateListDrawable +import android.os.Build import android.view.LayoutInflater import android.view.ViewGroup import androidx.core.view.isVisible @@ -49,13 +52,30 @@ class AccountEditColorAdapter @Inject constructor() : } } - private fun Int.createForegroundDrawable(): Drawable { - val mask = GradientDrawable().apply { - shape = GradientDrawable.OVAL - setColor(Color.BLACK) + private fun Int.createForegroundDrawable(): Drawable = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + val mask = GradientDrawable().apply { + shape = GradientDrawable.OVAL + setColor(Color.BLACK) + } + RippleDrawable(ColorStateList.valueOf(this.rippleColor), null, mask) + } else { + val foreground = StateListDrawable().apply { + alpha = 80 + setEnterFadeDuration(250) + setExitFadeDuration(250) + } + + val mask = GradientDrawable().apply { + shape = GradientDrawable.OVAL + setColor(this@createForegroundDrawable.rippleColor) + } + + foreground.apply { + addState(intArrayOf(android.R.attr.state_pressed), mask) + addState(intArrayOf(), ColorDrawable(Color.TRANSPARENT)) + } } - return RippleDrawable(ColorStateList.valueOf(this.rippleColor), null, mask) - } private inline val Int.rippleColor: Int get() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt index 4229579c..21a7a492 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt @@ -1,16 +1,14 @@ package io.github.wulkanowy.ui.modules.account.accountedit -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf +import android.view.ViewGroup import androidx.recyclerview.widget.GridLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.databinding.DialogAccountEditBinding import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.serializable import javax.inject.Inject @AndroidEntryPoint @@ -26,21 +24,28 @@ class AccountEditDialog : BaseDialogFragment(), Accoun private const val ARGUMENT_KEY = "student_with_semesters" - fun newInstance(student: Student) = AccountEditDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to student) - } + fun newInstance(student: Student) = + AccountEditDialog().apply { + arguments = Bundle().apply { + putSerializable(ARGUMENT_KEY, student) + } + } } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogAccountEditBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View = DialogAccountEditBinding.inflate(inflater).apply { binding = this }.root + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - presenter.onAttachView(this, requireArguments().serializable(ARGUMENT_KEY)) + presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student) } override fun initView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt index c401158e..67ecdb5f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt @@ -1,12 +1,15 @@ package io.github.wulkanowy.ui.modules.account.accountedit -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -35,26 +38,43 @@ class AccountEditPresenter @Inject constructor( } private fun loadData() { - resourceFlow { studentRepository.getStudentById(student.id, false).avatarColor } - .logResourceStatus("load student") - .onResourceSuccess { view?.updateSelectedColorData(it.toInt()) } - .onResourceError(errorHandler::dispatch) - .launch("load_data") + flowWithResource { + studentRepository.getStudentById(student.id, false).avatarColor + }.onEach { resource -> + when (resource.status) { + Status.LOADING -> Timber.i("Attempt to load student") + Status.SUCCESS -> { + view?.updateSelectedColorData(resource.data?.toInt()!!) + Timber.i("Attempt to load student: Success") + } + Status.ERROR -> { + Timber.i("Attempt to load student: An exception occurred") + errorHandler.dispatch(resource.error!!) + } + } + }.launch("load_data") } fun changeStudentNickAndAvatar(nick: String, avatarColor: Int) { - resourceFlow { - val studentNick = StudentNickAndAvatar( - nick = nick.trim(), - avatarColor = avatarColor.toLong() - ).apply { id = student.id } - + flowWithResource { + val studentNick = + StudentNickAndAvatar(nick = nick.trim(), avatarColor = avatarColor.toLong()) + .apply { id = student.id } studentRepository.updateStudentNickAndAvatar(studentNick) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Attempt to change a student nick and avatar") + Status.SUCCESS -> { + Timber.i("Change a student nick and avatar result: Success") + view?.recreateMainView() + } + Status.ERROR -> { + Timber.i("Change a student nick and avatar result: An exception occurred") + errorHandler.dispatch(it.error!!) + } + } } - .logResourceStatus("change student nick and avatar") - .onResourceSuccess { view?.recreateMainView() } - .onResourceNotLoading { view?.popView() } - .onResourceError(errorHandler::dispatch) + .afterLoading { view?.popView() } .launch("update_student") } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt index 2d2dccec..4279102e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt @@ -1,11 +1,10 @@ package io.github.wulkanowy.ui.modules.account.accountquick -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf +import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.DialogAccountQuickBinding @@ -14,7 +13,6 @@ import io.github.wulkanowy.ui.modules.account.AccountAdapter import io.github.wulkanowy.ui.modules.account.AccountFragment import io.github.wulkanowy.ui.modules.account.AccountItem import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.utils.serializable import javax.inject.Inject @AndroidEntryPoint @@ -32,23 +30,27 @@ class AccountQuickDialog : BaseDialogFragment(), Acco fun newInstance(studentsWithSemesters: List) = AccountQuickDialog().apply { - arguments = bundleOf(STUDENTS_ARGUMENT_KEY to studentsWithSemesters.toTypedArray()) + arguments = Bundle().apply { + putSerializable(STUDENTS_ARGUMENT_KEY, studentsWithSemesters.toTypedArray()) + } } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView( - DialogAccountQuickBinding.inflate(layoutInflater) - .apply { binding = this }.root - ) - .create() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogAccountQuickBinding.inflate(inflater).apply { binding = this }.root + + @Suppress("UNCHECKED_CAST") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - val studentsWithSemesters = requireArguments() - .serializable>(STUDENTS_ARGUMENT_KEY).toList() + val studentsWithSemesters = + (requireArguments()[STUDENTS_ARGUMENT_KEY] as Array).toList() presenter.onAttachView(this, studentsWithSemesters) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt index 32c07f80..39d8fce2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt @@ -1,11 +1,14 @@ package io.github.wulkanowy.ui.modules.account.accountquick -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.account.AccountItem +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -40,11 +43,21 @@ class AccountQuickPresenter @Inject constructor( return } - resourceFlow { studentRepository.switchStudent(studentWithSemesters) } - .logResourceStatus("change student") - .onResourceSuccess { view?.recreateMainView() } - .onResourceNotLoading { view?.popView() } - .onResourceError(errorHandler::dispatch) + flowWithResource { studentRepository.switchStudent(studentWithSemesters) } + .onEach { + when (it.status) { + Status.LOADING -> Timber.i("Attempt to change a student") + Status.SUCCESS -> { + Timber.i("Change a student result: Success") + view?.recreateMainView() + } + Status.ERROR -> { + Timber.i("Change a student result: An exception occurred") + errorHandler.dispatch(it.error!!) + } + } + } + .afterLoading { view?.popView() } .launch("switch") } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt index 39f376f6..6cee2396 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt @@ -9,7 +9,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.enums.SentExcuseStatus import io.github.wulkanowy.databinding.ItemAttendanceBinding -import io.github.wulkanowy.utils.descriptionRes +import io.github.wulkanowy.utils.description import io.github.wulkanowy.utils.isExcusableOrNotExcused import javax.inject.Inject @@ -35,11 +35,9 @@ class AttendanceAdapter @Inject constructor() : with(holder.binding) { attendanceItemNumber.text = item.number.toString() - attendanceItemSubject.text = item.subject.ifBlank { - root.context.getString(R.string.all_no_data) - } - attendanceItemDescription.setText(item.descriptionRes) - attendanceItemAlert.isVisible = item.let { it.absence && !it.excused } + attendanceItemSubject.text = item.subject + attendanceItemDescription.setText(item.description) + attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE } attendanceItemNumber.visibility = View.GONE attendanceItemExcuseInfo.visibility = View.GONE attendanceItemExcuseCheckbox.visibility = View.GONE @@ -48,7 +46,7 @@ class AttendanceAdapter @Inject constructor() : onExcuseCheckboxSelect(item, checked) } - when (item.excuseStatus?.let { SentExcuseStatus.valueOf(it) }) { + when (if (item.excuseStatus != null) SentExcuseStatus.valueOf(item.excuseStatus) else null) { SentExcuseStatus.WAITING -> { attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_waiting) attendanceItemExcuseInfo.visibility = View.VISIBLE diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt index c0026bee..d816d8f0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt @@ -1,20 +1,19 @@ package io.github.wulkanowy.ui.modules.attendance -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.databinding.DialogAttendanceBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.descriptionRes -import io.github.wulkanowy.utils.serializable +import io.github.wulkanowy.utils.description +import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.toFormattedString -@AndroidEntryPoint -class AttendanceDialog : BaseDialogFragment() { +class AttendanceDialog : DialogFragment() { + + private var binding: DialogAttendanceBinding by lifecycleAwareVariable() private lateinit var attendance: Attendance @@ -23,27 +22,30 @@ class AttendanceDialog : BaseDialogFragment() { private const val ARGUMENT_KEY = "Item" fun newInstance(exam: Attendance) = AttendanceDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to exam) + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - attendance = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + attendance = getSerializable(ARGUMENT_KEY) as Attendance + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogAttendanceBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogAttendanceBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) with(binding) { attendanceDialogSubjectValue.text = attendance.subject - attendanceDialogDescriptionValue.setText(attendance.descriptionRes) + attendanceDialogDescriptionValue.setText(attendance.description) attendanceDialogDateValue.text = attendance.date.toFormattedString() attendanceDialogNumberValue.text = attendance.number.toString() attendanceDialogClose.setOnClickListener { dismiss() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt index a73c2606..05894679 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt @@ -2,12 +2,19 @@ package io.github.wulkanowy.ui.modules.attendance import android.content.DialogInterface.BUTTON_POSITIVE import android.os.Bundle -import android.view.* -import android.view.View.* +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import androidx.appcompat.app.AlertDialog import androidx.appcompat.view.ActionMode -import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.datepicker.CalendarConstraints +import com.google.android.material.datepicker.MaterialDatePicker import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Attendance @@ -19,10 +26,12 @@ import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.SchoolDaysValidator import io.github.wulkanowy.utils.dpToPx -import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.openMaterialDatePicker +import io.github.wulkanowy.utils.schoolYearStart +import io.github.wulkanowy.utils.toLocalDateTime +import io.github.wulkanowy.utils.toTimestamp import java.time.LocalDate import javax.inject.Inject @@ -62,7 +71,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag private val actionModeCallback = object : ActionMode.Callback { override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { val inflater = mode.menuInflater - inflater.inflate(R.menu.context_menu_attendance, menu) + inflater.inflate(R.menu.context_menu_excuse, menu) return true } @@ -84,7 +93,6 @@ class AttendanceFragment : BaseFragment(R.layout.frag } } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -113,7 +121,9 @@ class AttendanceFragment : BaseFragment(R.layout.frag attendanceSwipe.setOnRefreshListener(presenter::onSwipeRefresh) attendanceSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) attendanceSwipe.setProgressBackgroundColorSchemeColor( - requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + requireContext().getThemeAttrColor( + R.attr.colorSwipeRefresh + ) ) attendanceErrorRetry.setOnClickListener { presenter.onRetry() } attendanceErrorDetails.setOnClickListener { presenter.onDetailsClick() } @@ -124,7 +134,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() } - attendanceNavContainer.elevation = requireContext().dpToPx(3f) + attendanceNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -208,27 +218,39 @@ class AttendanceFragment : BaseFragment(R.layout.frag } override fun showExcuseButton(show: Boolean) { - binding.attendanceExcuseButton.isVisible = show + binding.attendanceExcuseButton.visibility = if (show) VISIBLE else GONE } override fun showAttendanceDialog(lesson: Attendance) { (activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson)) } - override fun showDatePickerDialog(selectedDate: LocalDate) { - openMaterialDatePicker( - selected = selectedDate, - rangeStart = selectedDate.firstSchoolDayInSchoolYear, - rangeEnd = LocalDate.now().plusWeeks(1), - onDateSelected = { - presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth) - } - ) + override fun showDatePickerDialog(currentDate: LocalDate) { + val baseDate = currentDate.schoolYearStart + val rangeStart = baseDate.toTimestamp() + val rangeEnd = LocalDate.now().plusWeeks(1).toTimestamp() + + val constraintsBuilder = CalendarConstraints.Builder().apply { + setValidator(SchoolDaysValidator(rangeStart, rangeEnd)) + setStart(rangeStart) + setEnd(rangeEnd) + } + val datePicker = MaterialDatePicker.Builder.datePicker() + .setCalendarConstraints(constraintsBuilder.build()) + .setSelection(currentDate.toTimestamp()) + .build() + + datePicker.addOnPositiveButtonClickListener { + val date = it.toLocalDateTime() + presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth) + } + + datePicker.show(this@AttendanceFragment.parentFragmentManager, null) } override fun showExcuseDialog() { val dialogBinding = DialogExcuseBinding.inflate(LayoutInflater.from(context)) - MaterialAlertDialogBuilder(requireContext()) + AlertDialog.Builder(requireContext()) .setTitle(R.string.attendance_excuse_title) .setView(dialogBinding.root) .setNegativeButton(android.R.string.cancel) { _, _ -> } @@ -265,16 +287,12 @@ class AttendanceFragment : BaseFragment(R.layout.frag } override fun showExcuseCheckboxes(show: Boolean) { - with(attendanceAdapter) { + attendanceAdapter.apply { excuseActionMode = show notifyDataSetChanged() } } - override fun showDayNavigation(show: Boolean) { - binding.attendanceNavContainer.isVisible = show - } - override fun finishActionMode() { actionMode?.finish() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index 26bfaf19..9a159812 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -1,7 +1,7 @@ package io.github.wulkanowy.ui.modules.attendance import android.annotation.SuppressLint -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.repositories.AttendanceRepository import io.github.wulkanowy.data.repositories.PreferencesRepository @@ -9,7 +9,18 @@ import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.flowWithResource +import io.github.wulkanowy.utils.flowWithResourceIn +import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday +import io.github.wulkanowy.utils.isExcusableOrNotExcused +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.nextSchoolDay +import io.github.wulkanowy.utils.previousOrSameSchoolDay +import io.github.wulkanowy.utils.previousSchoolDay +import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEach @@ -91,19 +102,15 @@ class AttendancePresenter @Inject constructor( fun onViewReselected() { Timber.i("Attendance view is reselected") - view?.let { view -> + view?.also { view -> if (view.currentStackSize == 1) { - baseDate = now().previousOrSameSchoolDay - - if (currentDate != baseDate) { - reloadView(baseDate) - loadData() - } else if (!view.isViewEmpty) { - view.resetView() + baseDate.also { + if (currentDate != it) { + reloadView(it) + loadData() + } else if (!view.isViewEmpty) view.resetView() } - } else { - view.popView() - } + } else view.popView() } } @@ -145,8 +152,6 @@ class AttendancePresenter @Inject constructor( fun onExcuseDialogSubmit(reason: String) { view?.finishActionMode() - if (attendanceToExcuseList.isEmpty()) return - if (isVulcanExcusedFunctionEnabled) { excuseAbsence( reason = reason.takeIf { it.isNotBlank() }, @@ -167,8 +172,6 @@ class AttendancePresenter @Inject constructor( view?.apply { showExcuseCheckboxes(true) showExcuseButton(false) - enableSwipe(false) - showDayNavigation(false) } attendanceToExcuseList.clear() return true @@ -178,8 +181,6 @@ class AttendancePresenter @Inject constructor( view?.apply { showExcuseCheckboxes(false) showExcuseButton(true) - enableSwipe(true) - showDayNavigation(true) } } @@ -206,77 +207,93 @@ class AttendancePresenter @Inject constructor( var isParent = false - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() isParent = student.isParent val semester = semesterRepository.getCurrentSemester(student) attendanceRepository.getAttendance( - student = student, - semester = semester, - start = currentDate, - end = currentDate, - forceRefresh = forceRefresh + student, + semester, + currentDate, + currentDate, + forceRefresh ) - } - .logResourceStatus("load attendance") - .onResourceLoading { - view?.showExcuseButton(false) - } - .mapResourceData { - if (prefRepository.isShowPresent) { - it - } else { - it.filter { item -> !item.presence } - }.sortedBy { item -> item.number } - } - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showEmpty(it.isEmpty()) - showContent(it.isNotEmpty()) - updateData(it) - } - } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - isVulcanExcusedFunctionEnabled = it.any { item -> item.excusable } - val anyExcusables = it.any { it.isExcusableOrNotExcused } - view?.showExcuseButton(anyExcusables && (isParent || isVulcanExcusedFunctionEnabled)) + }.onEach { + when (it.status) { + Status.LOADING -> { + view?.showExcuseButton(false) + if (!it.data.isNullOrEmpty()) { + val filteredAttendance = if (prefRepository.isShowPresent) { + it.data + } else { + it.data.filter { item -> !item.presence } + } - analytics.logEvent( - "load_data", - "type" to "attendance", - "items" to it.size - ) - } - .onResourceNotLoading { - view?.run { - showRefresh(false) - showProgress(false) - enableSwipe(true) + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showEmpty(filteredAttendance.isEmpty()) + showContent(filteredAttendance.isNotEmpty()) + updateData(filteredAttendance.sortedBy { item -> item.number }) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading attendance result: Success") + val filteredAttendance = if (prefRepository.isShowPresent) { + it.data.orEmpty() + } else { + it.data?.filter { item -> !item.presence }.orEmpty() + } + + isVulcanExcusedFunctionEnabled = + filteredAttendance.any { item -> item.excusable } + + view?.apply { + updateData(filteredAttendance.sortedBy { item -> item.number }) + showEmpty(filteredAttendance.isEmpty()) + showErrorView(false) + showContent(filteredAttendance.isNotEmpty()) + showExcuseButton(filteredAttendance.any { item -> + (!isParent && isVulcanExcusedFunctionEnabled) || (isParent && item.isExcusableOrNotExcused) + }) + } + analytics.logEvent( + "load_data", + "type" to "attendance", + "items" to it.data!!.size + ) + } + Status.ERROR -> { + Timber.i("Loading attendance result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceError(errorHandler::dispatch) - .launch() + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + } + }.launch() } private fun excuseAbsence(reason: String?, toExcuseList: List) { - resourceFlow { + flowWithResource { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) attendanceRepository.excuseForAbsence(student, semester, toExcuseList, reason) }.onEach { - when (it) { - is Resource.Loading -> view?.run { + when (it.status) { + Status.LOADING -> view?.run { Timber.i("Excusing absence started") showProgress(true) showContent(false) showExcuseButton(false) } - is Resource.Success -> { + Status.SUCCESS -> { Timber.i("Excusing for absence result: Success") analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size) attendanceToExcuseList.clear() @@ -288,9 +305,9 @@ class AttendancePresenter @Inject constructor( } loadData(forceRefresh = true) } - is Resource.Error -> { + Status.ERROR -> { Timber.i("Excusing for absence result: An exception occurred") - errorHandler.dispatch(it.error) + errorHandler.dispatch(it.error!!) loadData() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt index b0123065..738f2ff8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt @@ -48,7 +48,7 @@ interface AttendanceView : BaseView { fun showAttendanceDialog(lesson: Attendance) - fun showDatePickerDialog(selectedDate: LocalDate) + fun showDatePickerDialog(currentDate: LocalDate) fun showExcuseDialog() @@ -60,8 +60,6 @@ interface AttendanceView : BaseView { fun showExcuseCheckboxes(show: Boolean) - fun showDayNavigation(show: Boolean) - fun finishActionMode() fun popView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt index e750b8d5..118971e6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt @@ -71,10 +71,10 @@ class AttendanceSummaryFragment : setOnItemSelectedListener { presenter.onSubjectSelected(it?.text?.toString()) } } - binding.attendanceSummarySubjectsContainer.elevation = requireContext().dpToPx(1f) + binding.attendanceSummarySubjectsContainer.setElevationCompat(requireContext().dpToPx(1f)) } - override fun updateSubjects(data: Collection) { + override fun updateSubjects(data: ArrayList) { with(subjectsAdapter) { clear() addAll(data) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt index 28199917..e53cda74 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.attendance.summary -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.Subject import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository @@ -10,6 +10,9 @@ import io.github.wulkanowy.data.repositories.SubjectRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.flow.onEach import timber.log.Timber import java.time.Month import javax.inject.Inject @@ -72,49 +75,53 @@ class AttendanceSummaryPresenter @Inject constructor( } private fun loadData(subjectId: Int, forceRefresh: Boolean = false) { + Timber.i("Loading attendance summary data started") + currentSubjectId = subjectId - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) - - attendanceSummaryRepository.getAttendanceSummary( - student = student, - semester = semester, - subjectId = subjectId, - forceRefresh = forceRefresh - ) - } - .logResourceStatus("load attendance summary") - .mapResourceData(this::sortItems) - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) - updateDataSet(it) + attendanceSummaryRepository.getAttendanceSummary(student, semester, subjectId, forceRefresh) + }.onEach { + when (it.status) { + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateDataSet(sortItems(it.data)) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading attendance summary result: Success") + view?.apply { + showEmpty(it.data!!.isEmpty()) + showContent(it.data.isNotEmpty()) + updateDataSet(sortItems(it.data)) + } + analytics.logEvent( + "load_data", + "type" to "attendance_summary", + "items" to it.data!!.size, + "item_id" to subjectId + ) + } + Status.ERROR -> { + Timber.i("Loading attendance summary result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "attendance_summary", - "items" to it.size, - "item_id" to subjectId - ) + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) } - .onResourceNotLoading { - view?.run { - showProgress(false) - showRefresh(false) - enableSwipe(true) - } - } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun sortItems(items: List) = items.sortedByDescending { item -> @@ -133,20 +140,27 @@ class AttendanceSummaryPresenter @Inject constructor( } private fun loadSubjects() { - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) subjectRepository.getSubjects(student, semester) - } - .logResourceStatus("load attendance summary subjects") - .onResourceData { - subjects = it - view?.run { - view?.updateSubjects(it.map { subject -> subject.name }.toList()) - showSubjects(true) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading attendance summary subjects started") + Status.SUCCESS -> { + subjects = it.data!! + + Timber.i("Loading attendance summary subjects result: Success") + view?.run { + view?.updateSubjects(ArrayList(it.data.map { subject -> subject.name })) + showSubjects(true) + } + } + Status.ERROR -> { + Timber.i("Loading attendance summary subjects result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceError(errorHandler::dispatch) - .launch("subjects") + }.launch("subjects") } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt index 99192f18..66f370c5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt @@ -25,7 +25,7 @@ interface AttendanceSummaryView : BaseView { fun updateDataSet(data: List) - fun updateSubjects(data: Collection) + fun updateSubjects(data: ArrayList) fun showSubjects(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthDialog.kt deleted file mode 100644 index fa29df47..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthDialog.kt +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.wulkanowy.ui.modules.auth - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.text.parseAsHtml -import androidx.core.view.isVisible -import androidx.core.widget.doOnTextChanged -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.databinding.DialogAuthBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import javax.inject.Inject - -@AndroidEntryPoint -class AuthDialog : BaseDialogFragment(), AuthView { - - @Inject - lateinit var presenter: AuthPresenter - - companion object { - fun newInstance() = AuthDialog() - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, R.style.FullScreenDialogStyle) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - return DialogAuthBinding.inflate(inflater).apply { binding = this }.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - presenter.onAttachView(this) - - binding.authInput.doOnTextChanged { text, _, _, _ -> - presenter.onPeselChange(text?.toString()) - } - - binding.authButton.setOnClickListener { presenter.authorize() } - binding.authSuccessButton.setOnClickListener { - activity?.recreate() - dismiss() - } - binding.authButtonSkip.setOnClickListener { dismiss() } - } - - override fun enableAuthButton(isEnabled: Boolean) { - binding.authButton.isEnabled = isEnabled - } - - override fun showProgress(show: Boolean) { - binding.authProgress.isVisible = show - } - - override fun showPeselError(show: Boolean) { - binding.authInputLayout.error = getString(R.string.auth_api_error).takeIf { show } - } - - override fun showInvalidPeselError(show: Boolean) { - binding.authInputLayout.error = getString(R.string.auth_invalid_error).takeIf { show } - } - - override fun showSuccess(show: Boolean) { - binding.authSuccess.isVisible = show - } - - override fun showContent(show: Boolean) { - binding.authForm.isVisible = show - } - - override fun showDescriptionWithName(name: String) { - binding.authDescription.text = getString(R.string.auth_description, name).parseAsHtml() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthPresenter.kt deleted file mode 100644 index 8f579712..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthPresenter.kt +++ /dev/null @@ -1,100 +0,0 @@ -package io.github.wulkanowy.ui.modules.auth - -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import kotlinx.coroutines.launch -import javax.inject.Inject - -class AuthPresenter @Inject constructor( - private val semesterRepository: SemesterRepository, - errorHandler: ErrorHandler, - studentRepository: StudentRepository -) : BasePresenter(errorHandler, studentRepository) { - - private var pesel: String = "" - - override fun onAttachView(view: AuthView) { - super.onAttachView(view) - view.enableAuthButton(pesel.length == 11) - view.showSuccess(false) - view.showProgress(false) - - loadName() - } - - private fun loadName() { - presenterScope.launch { - runCatching { studentRepository.getCurrentStudent(false) } - .onSuccess { view?.showDescriptionWithName(it.studentName) } - .onFailure { errorHandler.dispatch(it) } - } - } - - fun onPeselChange(newPesel: String?) { - pesel = newPesel.orEmpty() - - view?.enableAuthButton(pesel.length == 11) - view?.showPeselError(false) - view?.showInvalidPeselError(false) - } - - fun authorize() { - presenterScope.launch { - view?.showProgress(true) - view?.showContent(false) - - if (!isValidPESEL(pesel)) { - view?.showInvalidPeselError(true) - view?.showProgress(false) - view?.showContent(true) - return@launch - } - - runCatching { - val student = studentRepository.getCurrentStudent() - val semester = semesterRepository.getCurrentSemester(student) - - val isSuccess = studentRepository.authorizePermission(student, semester, pesel) - if (isSuccess) { - studentRepository.refreshStudentName(student, semester) - } - isSuccess - } - .onFailure { errorHandler.dispatch(it) } - .onSuccess { - if (it) { - view?.showSuccess(true) - view?.showContent(false) - view?.showPeselError(false) - } else { - view?.showSuccess(false) - view?.showContent(true) - view?.showPeselError(true) - } - } - - view?.showProgress(false) - } - } - - private fun isValidPESEL(peselString: String): Boolean { - if (peselString.length != 11) { - return false - } - - val weights = intArrayOf(1, 3, 7, 9, 1, 3, 7, 9, 1, 3) - var sum = 0 - - for (i in 0 until 10) { - sum += weights[i] * Character.getNumericValue(peselString[i]) - } - - sum %= 10 - sum = 10 - sum - sum %= 10 - - return sum == Character.getNumericValue(peselString[10]) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthView.kt deleted file mode 100644 index d7e1917c..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthView.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.ui.modules.auth - -import io.github.wulkanowy.ui.base.BaseView - -interface AuthView : BaseView { - - fun enableAuthButton(isEnabled: Boolean) - - fun showProgress(show: Boolean) - - fun showPeselError(show: Boolean) - - fun showInvalidPeselError(show: Boolean) - - fun showSuccess(show: Boolean) - - fun showContent(show: Boolean) - - fun showDescriptionWithName(name: String) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt index f63b293c..c8728614 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt @@ -14,8 +14,6 @@ class ConferenceAdapter @Inject constructor() : var items = emptyList() - var onItemClickListener: (Conference) -> Unit = {} - override fun getItemCount() = items.size override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( @@ -30,10 +28,7 @@ class ConferenceAdapter @Inject constructor() : conferenceItemTitle.text = item.title conferenceItemSubject.text = item.subject conferenceItemContent.text = item.agenda - conferenceItemContent.visibility = - if (item.agenda.isBlank()) View.GONE else View.VISIBLE - - root.setOnClickListener { onItemClickListener(item) } + conferenceItemContent.visibility = if (item.agenda.isBlank()) View.GONE else View.VISIBLE } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt deleted file mode 100644 index c532377e..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.wulkanowy.ui.modules.conference - -import android.app.Dialog -import android.os.Bundle -import android.view.View -import androidx.core.os.bundleOf -import androidx.core.view.isVisible -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.data.db.entities.Conference -import io.github.wulkanowy.databinding.DialogConferenceBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.serializable -import io.github.wulkanowy.utils.toFormattedString - -@AndroidEntryPoint -class ConferenceDialog : BaseDialogFragment() { - - private lateinit var conference: Conference - - companion object { - - private const val ARGUMENT_KEY = "item" - - fun newInstance(conference: Conference) = ConferenceDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to conference) - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - conference = requireArguments().serializable(ARGUMENT_KEY) - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogConferenceBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - with(binding) { - conferenceDialogClose.setOnClickListener { dismiss() } - - conferenceDialogSubjectValue.text = conference.subject - conferenceDialogDateValue.text = conference.date.toFormattedString("dd.MM.yyyy HH:mm") - conferenceDialogHeaderValue.text = conference.title - conferenceDialogAgendaValue.text = conference.agenda - conferenceDialogPresentValue.text = conference.presentOnConference - conferenceDialogPresentValue.isVisible = conference.presentOnConference.isNotBlank() - conferenceDialogPresentTitle.isVisible = conference.presentOnConference.isNotBlank() - conferenceDialogAgendaValue.isVisible = conference.agenda.isNotBlank() - conferenceDialogAgendaTitle.isVisible = conference.agenda.isNotBlank() - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt index 0cd3150c..dd10a65e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt @@ -8,7 +8,6 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Conference import io.github.wulkanowy.databinding.FragmentConferenceBinding import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.widgets.DividerItemDecoration import io.github.wulkanowy.utils.getThemeAttrColor @@ -16,7 +15,7 @@ import javax.inject.Inject @AndroidEntryPoint class ConferenceFragment : BaseFragment(R.layout.fragment_conference), - ConferenceView, MainView.TitledView, MainView.MainChildView { + ConferenceView, MainView.TitledView { @Inject lateinit var presenter: ConferencePresenter @@ -42,8 +41,6 @@ class ConferenceFragment : BaseFragment(R.layout.frag } override fun initView() { - conferencesAdapter.onItemClickListener = presenter::onItemSelected - with(binding.conferenceRecycler) { layoutManager = LinearLayoutManager(context) adapter = conferencesAdapter @@ -53,11 +50,7 @@ class ConferenceFragment : BaseFragment(R.layout.frag with(binding) { conferenceSwipe.setOnRefreshListener(presenter::onSwipeRefresh) conferenceSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) - conferenceSwipe.setProgressBackgroundColorSchemeColor( - requireContext().getThemeAttrColor( - R.attr.colorSwipeRefresh - ) - ) + conferenceSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) conferenceErrorRetry.setOnClickListener { presenter.onRetry() } conferenceErrorDetails.setOnClickListener { presenter.onDetailsClick() } } @@ -105,18 +98,6 @@ class ConferenceFragment : BaseFragment(R.layout.frag binding.conferenceRecycler.visibility = if (show) View.VISIBLE else View.GONE } - override fun openConferenceDialog(conference: Conference) { - (activity as? MainActivity)?.showDialogFragment(ConferenceDialog.newInstance(conference)) - } - - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onFragmentReselected() - } - - override fun resetView() { - binding.conferenceRecycler.smoothScrollToPosition(0) - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt index 1178c720..cc7e50db 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt @@ -1,13 +1,15 @@ package io.github.wulkanowy.ui.modules.conference -import io.github.wulkanowy.data.* -import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.ConferenceRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -41,10 +43,6 @@ class ConferencePresenter @Inject constructor( loadData(true) } - fun onItemSelected(conference: Conference) { - view?.openConferenceDialog(conference) - } - fun onDetailsClick() { view?.showErrorDetailsDialog(lastError) } @@ -61,46 +59,50 @@ class ConferencePresenter @Inject constructor( } private fun loadData(forceRefresh: Boolean = false) { - flatResourceFlow { + Timber.i("Loading conference data started") + + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) conferenceRepository.getConferences(student, semester, forceRefresh) - } - .logResourceStatus("load conference data") - .mapResourceData { it.sortedByDescending { conference -> conference.date } } - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) - updateData(it) + }.onEach { + when (it.status) { + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data.sortedByDescending { conference -> conference.date }) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading conference result: Success") + view?.run { + updateData(it.data!!.sortedByDescending { conference -> conference.date }) + showContent(it.data.isNotEmpty()) + showEmpty(it.data.isEmpty()) + showErrorView(false) + } + analytics.logEvent( + "load_data", + "type" to "conferences", + "items" to it.data!!.size + ) + } + Status.ERROR -> { + Timber.i("Loading conference result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "conferences", - "items" to it.size - ) + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showProgress(false) - showRefresh(false) - } - } - .onResourceError(errorHandler::dispatch) - .launch() - } - - fun onFragmentReselected() { - Timber.i("Conference is reselected") - if (view?.isViewEmpty == false) { - view?.resetView() - } + }.launch() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt index 3299a1f0..f3d1b3b3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt @@ -26,8 +26,4 @@ interface ConferenceView : BaseView { fun enableSwipe(enable: Boolean) fun showContent(show: Boolean) - - fun openConferenceDialog(conference: Conference) - - fun resetView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt similarity index 71% rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt index 4ad4e9d6..fa081ce7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt @@ -1,15 +1,12 @@ -package io.github.wulkanowy.ui.modules.dashboard.adapters +package io.github.wulkanowy.ui.modules.dashboard 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 import android.view.LayoutInflater import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.text.parseAsHtml import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.core.view.updateMarginsRelative @@ -17,20 +14,26 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.AdminMessage -import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Timetable 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.utils.* -import timber.log.Timber +import io.github.wulkanowy.databinding.ItemDashboardAccountBinding +import io.github.wulkanowy.databinding.ItemDashboardAnnouncementsBinding +import io.github.wulkanowy.databinding.ItemDashboardConferencesBinding +import io.github.wulkanowy.databinding.ItemDashboardExamsBinding +import io.github.wulkanowy.databinding.ItemDashboardGradesBinding +import io.github.wulkanowy.databinding.ItemDashboardHomeworkBinding +import io.github.wulkanowy.databinding.ItemDashboardHorizontalGroupBinding +import io.github.wulkanowy.databinding.ItemDashboardLessonsBinding +import io.github.wulkanowy.utils.createNameInitialsDrawable +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.left +import io.github.wulkanowy.utils.nickOrName +import io.github.wulkanowy.utils.toFormattedString import java.time.Duration -import java.time.Instant import java.time.LocalDate import java.time.LocalDateTime -import java.util.* +import java.util.Timer import javax.inject.Inject import kotlin.concurrent.timer @@ -38,7 +41,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter Unit = {} + var onAccountTileClickListener: () -> Unit = {} var onLuckyNumberTileClickListener: () -> Unit = {} @@ -48,7 +51,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter Unit = {} - var onLessonsTileClickListener: (LocalDate) -> Unit = {} + var onLessonsTileClickListener: () -> Unit = {} var onHomeworkTileClickListener: () -> Unit = {} @@ -58,10 +61,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter Unit = {} - var onAdminMessageClickListener: (String?) -> Unit = {} - - var onAdminMessageDismissClickListener: (AdminMessage) -> Unit = {} - val items = mutableListOf() fun submitList(newItems: List) { @@ -108,12 +107,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter ConferencesViewHolder( ItemDashboardConferencesBinding.inflate(inflater, parent, false) ) - DashboardItem.Type.ADMIN_MESSAGE.ordinal -> AdminMessageViewHolder( - ItemDashboardAdminMessageBinding.inflate(inflater, parent, false) - ) - DashboardItem.Type.ADS.ordinal -> AdsViewHolder( - ItemDashboardAdsBinding.inflate(inflater, parent, false) - ) else -> throw IllegalArgumentException() } } @@ -128,8 +121,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter bindAnnouncementsViewHolder(holder, position) is ExamsViewHolder -> bindExamsViewHolder(holder, position) is ConferencesViewHolder -> bindConferencesViewHolder(holder, position) - is AdminMessageViewHolder -> bindAdminMessage(holder, position) - is AdsViewHolder -> bindAdsViewHolder(holder, position) } } @@ -161,7 +152,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { - updateMarginsRelative( - end = if (isAttendanceHidden && isMessagesHidden && !isLuckyNumberHidden) { - 0 - } else context.dpToPx(8f).toInt() - ) - } - } - } - - private fun ItemDashboardHorizontalGroupBinding.bindMessages( - item: DashboardItem.HorizontalGroup, - isWideErrorShow: Boolean - ) { - dashboardHorizontalGroupItemMessageError.isVisible = item.unreadMessagesCount?.error == true - with(dashboardHorizontalGroupItemMessageValue) { - isVisible = item.unreadMessagesCount?.error != true - text = item.unreadMessagesCount?.data.toString() - } - with(dashboardHorizontalGroupItemMessageContainer) { - isVisible = item.unreadMessagesCount?.isHidden == false && !isWideErrorShow - setOnClickListener { onMessageTileClickListener() } - } - } - - private fun ItemDashboardHorizontalGroupBinding.bindAttendance( - item: DashboardItem.HorizontalGroup, - isWideErrorShow: Boolean - ) { - val attendancePercentage = item.attendancePercentage?.data + val unreadMessagesCount = item.unreadMessagesCount + val attendancePercentage = item.attendancePercentage + val luckyNumber = item.luckyNumber + val error = item.error + val isLoading = item.isLoading + val binding = horizontalGroupViewHolder.binding + val context = binding.root.context val attendanceColor = when { - attendancePercentage == null || attendancePercentage == .0 -> { - root.context.getThemeAttrColor(R.attr.colorOnSurface) + attendancePercentage ?: 0.0 <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> { + context.getThemeAttrColor(R.attr.colorPrimary) } - attendancePercentage <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> { - root.context.getThemeAttrColor(R.attr.colorPrimary) + attendancePercentage ?: 0.0 <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> { + context.getThemeAttrColor(R.attr.colorTimetableChange) } - attendancePercentage <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> { - root.context.getThemeAttrColor(R.attr.colorTimetableChange) - } - else -> root.context.getThemeAttrColor(R.attr.colorOnSurface) - } - val attendanceString = if (attendancePercentage == null || attendancePercentage == .0) { - root.context.getString(R.string.dashboard_horizontal_group_no_data) - } else { - "%.2f%%".format(attendancePercentage) + else -> context.getThemeAttrColor(R.attr.colorOnSurface) } - dashboardHorizontalGroupItemAttendanceError.isVisible = - item.attendancePercentage?.error == true - with(dashboardHorizontalGroupItemAttendanceValue) { - isVisible = item.attendancePercentage?.error != true - text = attendanceString + with(binding.dashboardHorizontalGroupItemAttendanceValue) { + text = "%.2f%%".format(attendancePercentage) setTextColor(attendanceColor) } - with(dashboardHorizontalGroupItemAttendanceContainer) { - isVisible = item.attendancePercentage?.isHidden == false && !isWideErrorShow - setOnClickListener { onAttendanceTileClickListener() } - updateLayoutParams { - matchConstraintPercentWidth = when { - item.luckyNumber?.isHidden == true && item.unreadMessagesCount?.isHidden == true -> 1.0f - item.luckyNumber?.isHidden == true || item.unreadMessagesCount?.isHidden == true -> 0.5f - else -> 0.4f + + with(binding) { + dashboardHorizontalGroupItemMessageValue.text = unreadMessagesCount.toString() + dashboardHorizontalGroupItemLuckyValue.text = if (luckyNumber == -1) { + context.getString(R.string.dashboard_horizontal_group_no_lukcy_number) + } else luckyNumber?.toString() + + if (dashboardHorizontalGroupItemInfoContainer.isVisible != (error != null || isLoading)) { + dashboardHorizontalGroupItemInfoContainer.isVisible = error != null || isLoading + } + + if (dashboardHorizontalGroupItemInfoProgress.isVisible != isLoading) { + dashboardHorizontalGroupItemInfoProgress.isVisible = isLoading + } + + dashboardHorizontalGroupItemInfoErrorText.isVisible = error != null + + with(dashboardHorizontalGroupItemLuckyContainer) { + isVisible = error == null && !isLoading && luckyNumber != null + setOnClickListener { onLuckyNumberTileClickListener() } + + updateLayoutParams { + updateMarginsRelative( + end = if (attendancePercentage == null && unreadMessagesCount == null && luckyNumber != null) { + 0 + } else { + context.dpToPx(8f).toInt() + } + ) } } + + with(dashboardHorizontalGroupItemAttendanceContainer) { + isVisible = error == null && !isLoading && attendancePercentage != null + updateLayoutParams { + matchConstraintPercentWidth = when { + luckyNumber == null && unreadMessagesCount == null -> 1.0f + luckyNumber == null || unreadMessagesCount == null -> 0.5f + else -> 0.4f + } + } + setOnClickListener { onAttendanceTileClickListener() } + } + + with(dashboardHorizontalGroupItemMessageContainer) { + isVisible = error == null && !isLoading && unreadMessagesCount != null + setOnClickListener { onMessageTileClickListener() } + } } } @@ -281,7 +242,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { - dateToNavigate = currentDate updateLessonView(item, currentTimetable, binding) binding.dashboardLessonsItemTitleTomorrow.isVisible = false - binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false - } - tomorrowTimetable.isNotEmpty() -> { - dateToNavigate = tomorrowDate - updateLessonView(item, tomorrowTimetable, binding) - binding.dashboardLessonsItemTitleTomorrow.isVisible = true - binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false } currentDayHeader != null && currentDayHeader.content.isNotBlank() -> { - dateToNavigate = currentDate updateLessonView(item, emptyList(), binding, currentDayHeader) binding.dashboardLessonsItemTitleTomorrow.isVisible = false - binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false + } + tomorrowTimetable.isNotEmpty() -> { + updateLessonView(item, tomorrowTimetable, binding) + binding.dashboardLessonsItemTitleTomorrow.isVisible = true } tomorrowDayHeader != null && tomorrowDayHeader.content.isNotBlank() -> { - dateToNavigate = tomorrowDate updateLessonView(item, emptyList(), binding, tomorrowDayHeader) binding.dashboardLessonsItemTitleTomorrow.isVisible = true - binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false } else -> { - dateToNavigate = currentDate updateLessonView(item, emptyList(), binding) - binding.dashboardLessonsItemTitleTomorrow.isVisible = false - binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = + binding.dashboardLessonsItemTitleTomorrow.isVisible = !(item.isLoading && item.error == null) } } @@ -370,7 +318,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter 60) { val formattedStartTime = firstLesson.start.toFormattedString("HH:mm") val formattedEndTime = firstLesson.end.toFormattedString("HH:mm") - firstTimeRangeText = "$formattedStartTime - $formattedEndTime" + firstTimeRangeText = "${formattedStartTime}-${formattedEndTime}" firstTimeText = "" isFirstTimeRangeVisible = true isFirstTimeVisible = false } else { - firstTimeText = context.getString( - R.string.timetable_time_until, - context.getString( - R.string.timetable_minutes, - minutesToStartLesson.toString() - ) + firstTimeText = context.resources.getQuantityString( + R.plurals.dashboard_timetable_first_lesson_time_in_minutes, + minutesToStartLesson.toInt(), + minutesToStartLesson ) firstTimeRangeText = "" @@ -476,17 +421,12 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { - 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 - - binding.dashboardAdminMessageItemContent.removeAllViews() - binding.dashboardAdminMessageItemContent.addView( - item.view, - ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) - ) - } - class AccountViewHolder(val binding: ItemDashboardAccountBinding) : RecyclerView.ViewHolder(binding.root) @@ -819,12 +717,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter, private val oldList: List diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAnnouncementsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt similarity index 95% rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAnnouncementsAdapter.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt index da588015..7a4c2b25 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAnnouncementsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.modules.dashboard.adapters +package io.github.wulkanowy.ui.modules.dashboard import android.view.LayoutInflater import android.view.ViewGroup @@ -33,4 +33,4 @@ class DashboardAnnouncementsAdapter : class ViewHolder(val binding: SubitemDashboardAnnouncementsBinding) : RecyclerView.ViewHolder(binding.root) -} +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardConferencesAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt similarity index 95% rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardConferencesAdapter.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt index 1244ff60..64cf599c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardConferencesAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.modules.dashboard.adapters +package io.github.wulkanowy.ui.modules.dashboard import android.view.LayoutInflater import android.view.ViewGroup @@ -33,4 +33,4 @@ class DashboardConferencesAdapter : class ViewHolder(val binding: SubitemDashboardConferencesBinding) : RecyclerView.ViewHolder(binding.root) -} +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardExamsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt similarity index 97% rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardExamsAdapter.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt index 583bf29d..060f224b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardExamsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.ui.modules.dashboard.adapters +package io.github.wulkanowy.ui.modules.dashboard import android.annotation.SuppressLint import android.view.LayoutInflater @@ -56,4 +56,4 @@ class DashboardExamsAdapter : class ViewHolder(val binding: SubitemDashboardExamsBinding) : RecyclerView.ViewHolder(binding.root) -} +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt index ce17c763..3392280b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt @@ -10,16 +10,13 @@ import androidx.core.view.isVisible import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.databinding.FragmentDashboardBinding import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment +import io.github.wulkanowy.ui.modules.account.AccountFragment import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment import io.github.wulkanowy.ui.modules.conference.ConferenceFragment -import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter import io.github.wulkanowy.ui.modules.exam.ExamFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.homework.HomeworkFragment @@ -27,10 +24,11 @@ import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.message.MessageFragment -import io.github.wulkanowy.ui.modules.notificationscenter.NotificationsCenterFragment import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment import io.github.wulkanowy.ui.modules.timetable.TimetableFragment -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.toFormattedString import java.time.LocalDate import javax.inject.Inject @@ -49,20 +47,11 @@ class DashboardFragment : BaseFragment(R.layout.fragme override var subtitleString = LocalDate.now().toFormattedString("EEEE, d MMMM yyyy").capitalise() - override val tileWidth: Int - get() { - val recyclerWidth = binding.dashboardRecycler.width - val margin = requireContext().dpToPx(24f).toInt() - - return ((recyclerWidth - margin) / resources.displayMetrics.density).toInt() - } - companion object { fun newInstance() = DashboardFragment() } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -81,13 +70,14 @@ class DashboardFragment : BaseFragment(R.layout.fragme override fun initView() { val mainActivity = requireActivity() as MainActivity val itemTouchHelper = ItemTouchHelper( - DashboardItemMoveCallback(dashboardAdapter, presenter::onDragAndDropEnd) + DashboardItemMoveCallback( + dashboardAdapter, + presenter::onDragAndDropEnd + ) ) dashboardAdapter.apply { - onAccountTileClickListener = { - mainActivity.pushView(AccountDetailsFragment.newInstance(it)) - } + onAccountTileClickListener = { mainActivity.pushView(AccountFragment.newInstance()) } onLuckyNumberTileClickListener = { mainActivity.pushView(LuckyNumberFragment.newInstance()) } @@ -95,9 +85,7 @@ class DashboardFragment : BaseFragment(R.layout.fragme onAttendanceTileClickListener = { mainActivity.pushView(AttendanceSummaryFragment.newInstance()) } - onLessonsTileClickListener = { - mainActivity.pushView(TimetableFragment.newInstance(it)) - } + onLessonsTileClickListener = { mainActivity.pushView(TimetableFragment.newInstance()) } onGradeTileClickListener = { mainActivity.pushView(GradeFragment.newInstance()) } onHomeworkTileClickListener = { mainActivity.pushView(HomeworkFragment.newInstance()) } onAnnouncementsTileClickListener = { @@ -107,14 +95,6 @@ class DashboardFragment : BaseFragment(R.layout.fragme onConferencesTileClickListener = { mainActivity.pushView(ConferenceFragment.newInstance()) } - onAdminMessageClickListener = presenter::onAdminMessageSelected - onAdminMessageDismissClickListener = presenter::onAdminMessageDismissed - - registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { - override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { - binding.dashboardRecycler.scrollToPosition(0) - } - }) } with(binding) { @@ -139,7 +119,6 @@ class DashboardFragment : BaseFragment(R.layout.fragme override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.dashboard_menu_tiles -> presenter.onDashboardTileSettingsSelected() - R.id.dashboard_menu_notifaction_list -> presenter.onNotificationsCenterSelected() else -> false } } @@ -149,7 +128,7 @@ class DashboardFragment : BaseFragment(R.layout.fragme val values = requireContext().resources.getStringArray(R.array.dashboard_tile_values) val selectedItemsState = values.map { value -> selectedItems.any { it.name == value } } - MaterialAlertDialogBuilder(requireContext()) + AlertDialog.Builder(requireContext()) .setTitle(R.string.pref_dashboard_appearance_tiles_title) .setMultiChoiceItems(entries, selectedItemsState.toBooleanArray()) { _, _, _ -> } .setPositiveButton(android.R.string.ok) { dialog, _ -> @@ -186,8 +165,8 @@ class DashboardFragment : BaseFragment(R.layout.fragme binding.dashboardErrorContainer.isVisible = show } - override fun setErrorDetails(error: Throwable) { - binding.dashboardErrorMessage.text = requireContext().resources.getErrorString(error) + override fun setErrorDetails(message: String) { + binding.dashboardErrorMessage.text = message } override fun resetView() { @@ -202,17 +181,9 @@ class DashboardFragment : BaseFragment(R.layout.fragme if (::presenter.isInitialized) presenter.onViewReselected() } - override fun openNotificationsCenterView() { - (requireActivity() as MainActivity).pushView(NotificationsCenterFragment.newInstance()) - } - - override fun openInternetBrowser(url: String) { - requireContext().openInternetBrowser(url) - } - override fun onDestroyView() { dashboardAdapter.clearTimers() presenter.onDetachView() super.onDestroyView() } -} +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt similarity index 79% rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt index d821de53..aeecf5bf 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt @@ -1,21 +1,18 @@ -package io.github.wulkanowy.ui.modules.dashboard.adapters +package io.github.wulkanowy.ui.modules.dashboard -import android.content.res.ColorStateList import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.enums.GradeColorTheme import io.github.wulkanowy.databinding.SubitemDashboardGradesBinding import io.github.wulkanowy.databinding.SubitemDashboardSmallGradeBinding import io.github.wulkanowy.utils.getBackgroundColor -import io.github.wulkanowy.utils.getCompatColor class DashboardGradesAdapter : RecyclerView.Adapter() { var items = listOf>>() - lateinit var gradeColorTheme: GradeColorTheme + var gradeTheme = "" override fun getItemCount() = items.size @@ -39,9 +36,7 @@ class DashboardGradesAdapter : RecyclerView.Adapter? = null, - val attendancePercentage: Cell? = null, - val luckyNumber: Cell? = null, + val unreadMessagesCount: Int? = null, + val attendancePercentage: Double? = null, + val luckyNumber: Int? = null, override val error: Throwable? = null, override val isLoading: Boolean = false ) : DashboardItem(Type.HORIZONTAL_GROUP) { - data class Cell( - val data: T?, - val error: Boolean, - val isLoading: Boolean, - ) { - val isHidden: Boolean - get() = data == null && !error && !isLoading - } - override val isDataLoaded - get() = unreadMessagesCount?.isLoading == false || attendancePercentage?.isLoading == false || luckyNumber?.isLoading == false - - val isFullDataLoaded - get() = luckyNumber?.isLoading != true && attendancePercentage?.isLoading != true && unreadMessagesCount?.isLoading != true + get() = unreadMessagesCount != null || attendancePercentage != null || luckyNumber != null } data class Grades( val subjectWithGrades: Map>? = null, - val gradeTheme: GradeColorTheme? = null, + val gradeTheme: String? = null, override val error: Throwable? = null, override val isLoading: Boolean = false ) : DashboardItem(Type.GRADES) { @@ -111,46 +92,34 @@ sealed class DashboardItem(val type: Type) { override val isDataLoaded get() = conferences != null } - data class Ads( - val adBanner: AdBanner? = null, - override val error: Throwable? = null, - override val isLoading: Boolean = false - ) : DashboardItem(Type.ADS) { - - override val isDataLoaded get() = adBanner != null - } - enum class Type { - ADMIN_MESSAGE, ACCOUNT, HORIZONTAL_GROUP, LESSONS, - ADS, GRADES, HOMEWORK, ANNOUNCEMENTS, EXAMS, CONFERENCES, + ADS } enum class Tile { - ADMIN_MESSAGE, ACCOUNT, LUCKY_NUMBER, MESSAGES, ATTENDANCE, LESSONS, - ADS, GRADES, HOMEWORK, ANNOUNCEMENTS, EXAMS, CONFERENCES, + ADS } } fun DashboardItem.Tile.toDashboardItemType() = when (this) { - DashboardItem.Tile.ADMIN_MESSAGE -> DashboardItem.Type.ADMIN_MESSAGE DashboardItem.Tile.ACCOUNT -> DashboardItem.Type.ACCOUNT DashboardItem.Tile.LUCKY_NUMBER -> DashboardItem.Type.HORIZONTAL_GROUP DashboardItem.Tile.MESSAGES -> DashboardItem.Type.HORIZONTAL_GROUP @@ -162,4 +131,4 @@ fun DashboardItem.Tile.toDashboardItemType() = when (this) { DashboardItem.Tile.EXAMS -> DashboardItem.Type.EXAMS DashboardItem.Tile.CONFERENCES -> DashboardItem.Type.CONFERENCES DashboardItem.Tile.ADS -> DashboardItem.Type.ADS -} +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt index 9c15acc3..cf4097a4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt @@ -2,8 +2,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 java.util.* +import java.util.Collections class DashboardItemMoveCallback( private val dashboardAdapter: DashboardAdapter, @@ -22,7 +21,7 @@ class DashboardItemMoveCallback( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder ): Int { - val dragFlags = if (!viewHolder.isAdminMessageOrAccountItem) { + val dragFlags = if (viewHolder.bindingAdapterPosition != 0) { ItemTouchHelper.UP or ItemTouchHelper.DOWN } else 0 @@ -33,7 +32,7 @@ class DashboardItemMoveCallback( recyclerView: RecyclerView, current: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder - ) = !target.isAdminMessageOrAccountItem + ) = target.bindingAdapterPosition != 0 override fun onMove( recyclerView: RecyclerView, @@ -53,7 +52,4 @@ class DashboardItemMoveCallback( onUserInteractionEndListener(dashboardAdapter.items.toList()) } - - private val RecyclerView.ViewHolder.isAdminMessageOrAccountItem: Boolean - get() = this is DashboardAdapter.AdminMessageViewHolder || this is DashboardAdapter.AccountViewHolder } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt index ac2c896d..0e24f0a1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt @@ -1,21 +1,31 @@ package io.github.wulkanowy.ui.modules.dashboard -import io.github.wulkanowy.data.* -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.Resource +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.enums.MessageFolder -import io.github.wulkanowy.data.repositories.* +import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository +import io.github.wulkanowy.data.repositories.ConferenceRepository +import io.github.wulkanowy.data.repositories.ExamRepository +import io.github.wulkanowy.data.repositories.GradeRepository +import io.github.wulkanowy.data.repositories.HomeworkRepository +import io.github.wulkanowy.data.repositories.LuckyNumberRepository +import io.github.wulkanowy.data.repositories.MessageRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository +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.AdsHelper import io.github.wulkanowy.utils.calculatePercentage +import io.github.wulkanowy.utils.flowWithResource +import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.nextOrSameSchoolDay -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import timber.log.Timber -import java.time.Instant import java.time.LocalDate +import java.time.LocalDateTime import javax.inject.Inject class DashboardPresenter @Inject constructor( @@ -31,20 +41,16 @@ class DashboardPresenter @Inject constructor( private val examRepository: ExamRepository, private val conferenceRepository: ConferenceRepository, private val preferencesRepository: PreferencesRepository, - private val schoolAnnouncementRepository: SchoolAnnouncementRepository, - private val adminMessageRepository: AdminMessageRepository, - private val adsHelper: AdsHelper + private val schoolAnnouncementRepository: SchoolAnnouncementRepository ) : BasePresenter(errorHandler, studentRepository) { private val dashboardItemLoadedList = mutableListOf() private val dashboardItemRefreshLoadedList = mutableListOf() - private var dashboardItemsToLoad = emptySet() + private lateinit var dashboardItemsToLoad: Set - private var dashboardTileLoadedList = emptySet() - - private val firstLoadedItemList = mutableListOf() + private var dashboardTilesToLoad: Set = emptySet() private lateinit var lastError: Throwable @@ -57,26 +63,14 @@ class DashboardPresenter @Inject constructor( showContent(false) } - merge( - preferencesRepository.selectedDashboardTilesFlow, - preferencesRepository.isAdsEnabledFlow - .map { preferencesRepository.selectedDashboardTiles } - ) + preferencesRepository.selectedDashboardTilesFlow .onEach { loadData(tilesToLoad = it) } .launch("dashboard_pref") } - fun onAdminMessageDismissed(adminMessage: AdminMessage) { - preferencesRepository.dismissedAdminMessageIds += adminMessage.id - - loadData(preferencesRepository.selectedDashboardTiles) - } - fun onDragAndDropEnd(list: List) { - with(dashboardItemLoadedList) { - clear() - addAll(list) - } + dashboardItemLoadedList.clear() + dashboardItemLoadedList.addAll(list) val positionList = list.mapIndexed { index, dashboardItem -> Pair(dashboardItem.type, index) }.toMap() @@ -84,104 +78,87 @@ class DashboardPresenter @Inject constructor( preferencesRepository.dashboardItemsPosition = positionList } - fun loadData( - tilesToLoad: Set, - forceRefresh: Boolean = false, - ) { - val oldDashboardTileLoadedList = dashboardTileLoadedList - dashboardItemsToLoad = tilesToLoad.map { it.toDashboardItemType() }.toSet() - dashboardTileLoadedList = tilesToLoad + fun loadData(forceRefresh: Boolean = false, tilesToLoad: Set) { + val oldDashboardDataToLoad = dashboardTilesToLoad - val itemsToLoad = generateDashboardTileListToLoad( - dashboardTilesToLoad = tilesToLoad, - dashboardLoadedTiles = oldDashboardTileLoadedList, - forceRefresh = forceRefresh - ).map { it.toDashboardItemType() } + dashboardTilesToLoad = tilesToLoad + dashboardItemsToLoad = dashboardTilesToLoad.map { it.toDashboardItemType() }.toSet() - removeUnselectedTiles(tilesToLoad.toList()) - loadTiles(tileList = itemsToLoad, forceRefresh = forceRefresh) + removeUnselectedTiles() + + val newTileList = generateTileListToLoad(oldDashboardDataToLoad, forceRefresh) + loadTiles(forceRefresh, newTileList) } - private fun generateDashboardTileListToLoad( - dashboardTilesToLoad: Set, - dashboardLoadedTiles: Set, - forceRefresh: Boolean - ) = dashboardTilesToLoad.filter { newItemToLoad -> - dashboardLoadedTiles.none { it == newItemToLoad } || forceRefresh - || newItemToLoad == DashboardItem.Tile.ADMIN_MESSAGE - } + private fun removeUnselectedTiles() { + val isLuckyNumberToLoad = + dashboardTilesToLoad.any { it == DashboardItem.Tile.LUCKY_NUMBER } + val isMessagesToLoad = + dashboardTilesToLoad.any { it == DashboardItem.Tile.MESSAGES } + val isAttendanceToLoad = + dashboardTilesToLoad.any { it == DashboardItem.Tile.ATTENDANCE } - private fun removeUnselectedTiles(tilesToLoad: List) { dashboardItemLoadedList.removeAll { loadedTile -> dashboardItemsToLoad.none { it == loadedTile.type } } val horizontalGroup = dashboardItemLoadedList.find { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup? if (horizontalGroup != null) { - val isLuckyNumberToLoad = DashboardItem.Tile.LUCKY_NUMBER in tilesToLoad - val isMessagesToLoad = DashboardItem.Tile.MESSAGES in tilesToLoad - val isAttendanceToLoad = DashboardItem.Tile.ATTENDANCE in tilesToLoad + val horizontalIndex = dashboardItemLoadedList.indexOf(horizontalGroup) + dashboardItemLoadedList.remove(horizontalGroup) - val horizontalGroupIndex = dashboardItemLoadedList.indexOf(horizontalGroup) + var updatedHorizontalGroup = horizontalGroup - val newHorizontalGroup = horizontalGroup.copy( - attendancePercentage = horizontalGroup.attendancePercentage.takeIf { isAttendanceToLoad }, - unreadMessagesCount = horizontalGroup.unreadMessagesCount.takeIf { isMessagesToLoad }, - luckyNumber = horizontalGroup.luckyNumber.takeIf { isLuckyNumberToLoad } - ) - - with(dashboardItemLoadedList) { - removeAt(horizontalGroupIndex) - add(horizontalGroupIndex, newHorizontalGroup) + if (horizontalGroup.luckyNumber != null && !isLuckyNumberToLoad) { + updatedHorizontalGroup = updatedHorizontalGroup.copy(luckyNumber = null) } + + if (horizontalGroup.attendancePercentage != null && !isAttendanceToLoad) { + updatedHorizontalGroup = updatedHorizontalGroup.copy(attendancePercentage = null) + } + + if (horizontalGroup.unreadMessagesCount != null && !isMessagesToLoad) { + updatedHorizontalGroup = updatedHorizontalGroup.copy(unreadMessagesCount = null) + } + + if (horizontalGroup.error != null) { + updatedHorizontalGroup = updatedHorizontalGroup.copy(error = null, isLoading = true) + } + + dashboardItemLoadedList.add(horizontalIndex, updatedHorizontalGroup) } view?.updateData(dashboardItemLoadedList) } - private fun loadTiles( - tileList: List, - forceRefresh: Boolean - ) { - presenterScope.launch { - Timber.i("Loading dashboard account data started") - val student = runCatching { studentRepository.getCurrentStudent(true) } - .onFailure { - Timber.i("Loading dashboard account result: An exception occurred") - errorHandler.dispatch(it) - updateData(DashboardItem.Account(error = it), forceRefresh) - } - .onSuccess { Timber.i("Loading dashboard account result: Success") } - .getOrNull() ?: return@launch - - tileList.forEach { - when (it) { - 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) - } + private fun loadTiles(forceRefresh: Boolean, tileList: List) { + tileList.forEach { + when (it) { + DashboardItem.Tile.ACCOUNT -> loadCurrentAccount(forceRefresh) + DashboardItem.Tile.LUCKY_NUMBER -> loadLuckyNumber(forceRefresh) + DashboardItem.Tile.MESSAGES -> loadMessages(forceRefresh) + DashboardItem.Tile.ATTENDANCE -> loadAttendance(forceRefresh) + DashboardItem.Tile.LESSONS -> loadLessons(forceRefresh) + DashboardItem.Tile.GRADES -> loadGrades(forceRefresh) + DashboardItem.Tile.HOMEWORK -> loadHomework(forceRefresh) + DashboardItem.Tile.ANNOUNCEMENTS -> loadSchoolAnnouncements(forceRefresh) + DashboardItem.Tile.EXAMS -> loadExams(forceRefresh) + DashboardItem.Tile.CONFERENCES -> loadConferences(forceRefresh) + DashboardItem.Tile.ADS -> TODO() } } } + private fun generateTileListToLoad( + oldDashboardTileToLoad: Set, + forceRefresh: Boolean + ) = dashboardTilesToLoad.filter { newTileToLoad -> + oldDashboardTileToLoad.none { it == newTileToLoad } || forceRefresh + } + fun onSwipeRefresh() { Timber.i("Force refreshing the dashboard") - loadData(preferencesRepository.selectedDashboardTiles, forceRefresh = true) + loadData(true, preferencesRepository.selectedDashboardTiles) } fun onRetry() { @@ -189,7 +166,7 @@ class DashboardPresenter @Inject constructor( showErrorView(false) showProgress(true) } - loadData(preferencesRepository.selectedDashboardTiles, forceRefresh = true) + loadData(true, preferencesRepository.selectedDashboardTiles) } fun onViewReselected() { @@ -204,11 +181,6 @@ class DashboardPresenter @Inject constructor( view?.showErrorDetailsDialog(lastError) } - fun onNotificationsCenterSelected(): Boolean { - view?.openNotificationsCenterView() - return true - } - fun onDashboardTileSettingsSelected(): Boolean { view?.showDashboardTileSettings(preferencesRepository.selectedDashboardTiles.toList()) return true @@ -220,163 +192,196 @@ class DashboardPresenter @Inject constructor( }.toSet() } - fun onAdminMessageSelected(url: String?) { - url?.let { view?.openInternetBrowser(it) } - } - - private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) { - flow { - val selectedTiles = preferencesRepository.selectedDashboardTiles - val flowSuccess = flowOf(Resource.Success(null)) - - val luckyNumberFlow = luckyNumberRepository.getLuckyNumber(student, forceRefresh) - .mapResourceData { - it ?: LuckyNumber(0, LocalDate.now(), 0) - } - .onResourceError { errorHandler.dispatch(it) } - .takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowSuccess - - val messageFLow = flatResourceFlow { - val mailbox = messageRepository.getMailboxByStudent(student) - - messageRepository.getMessages( - student = student, - mailbox = mailbox, - folder = MessageFolder.RECEIVED, - forceRefresh = forceRefresh - ) - } - .onResourceError { errorHandler.dispatch(it) } - .takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess - - val attendanceFlow = flatResourceFlow { - val semester = semesterRepository.getCurrentSemester(student) - attendanceSummaryRepository.getAttendanceSummary( - student = student, - semester = semester, - subjectId = -1, - forceRefresh = forceRefresh - ) - } - .onResourceError { errorHandler.dispatch(it) } - .takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess - - emitAll( - combine( - flow = luckyNumberFlow, - flow2 = messageFLow, - flow3 = attendanceFlow, - ) { luckyNumberResource, messageResource, attendanceResource -> - val resList = listOf(luckyNumberResource, messageResource, attendanceResource) - - DashboardItem.HorizontalGroup( - isLoading = resList.any { it is Resource.Loading }, - error = resList.map { it.errorOrNull }.let { errors -> - if (errors.all { it != null }) { - errors.firstOrNull() - } else null - }, - attendancePercentage = DashboardItem.HorizontalGroup.Cell( - data = attendanceResource.dataOrNull?.calculatePercentage(), - error = attendanceResource.errorOrNull != null, - isLoading = attendanceResource is Resource.Loading, - ), - unreadMessagesCount = DashboardItem.HorizontalGroup.Cell( - data = messageResource.dataOrNull?.count { it.unread }, - error = messageResource.errorOrNull != null, - isLoading = messageResource is Resource.Loading, - ), - luckyNumber = DashboardItem.HorizontalGroup.Cell( - data = luckyNumberResource.dataOrNull?.luckyNumber, - error = luckyNumberResource.errorOrNull != null, - isLoading = luckyNumberResource is Resource.Loading, - ) - ) - }) - } - .filterNot { it.isLoading && forceRefresh } - .distinctUntilChanged() + private fun loadCurrentAccount(forceRefresh: Boolean) { + flowWithResource { studentRepository.getCurrentStudent(false) } .onEach { - updateData(it, forceRefresh) - - if (it.isLoading) { - Timber.i("Loading horizontal group data started") - } else { - firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP - Timber.i("Loading horizontal group result: Success") + when (it.status) { + Status.LOADING -> { + Timber.i("Loading dashboard account data started") + if (forceRefresh) return@onEach + updateData(DashboardItem.Account(it.data, isLoading = true), forceRefresh) + } + Status.SUCCESS -> { + Timber.i("Loading dashboard account result: Success") + updateData(DashboardItem.Account(it.data), forceRefresh) + } + Status.ERROR -> { + Timber.i("Loading dashboard account result: An exception occurred") + errorHandler.dispatch(it.error!!) + updateData(DashboardItem.Account(error = it.error), forceRefresh) + } } } - .catch { - Timber.i("Loading horizontal group result: An exception occurred") - updateData( - DashboardItem.HorizontalGroup(error = it), - forceRefresh, - ) - errorHandler.dispatch(it) - } - .launch("horizontal_group ${if (forceRefresh) "-forceRefresh" else ""}") + .launch("dashboard_account") } - private fun loadGrades(student: Student, forceRefresh: Boolean) { - flatResourceFlow { + private fun loadLuckyNumber(forceRefresh: Boolean) { + flowWithResourceIn { + val student = studentRepository.getCurrentStudent(true) + + luckyNumberRepository.getLuckyNumber(student, forceRefresh) + }.onEach { + when (it.status) { + Status.LOADING -> { + Timber.i("Loading dashboard lucky number data started") + if (forceRefresh) return@onEach + processHorizontalGroupData( + luckyNumber = it.data?.luckyNumber, + isLoading = true, + forceRefresh = forceRefresh + ) + } + Status.SUCCESS -> { + Timber.i("Loading dashboard lucky number result: Success") + processHorizontalGroupData( + luckyNumber = it.data?.luckyNumber ?: -1, + forceRefresh = forceRefresh + ) + } + Status.ERROR -> { + Timber.i("Loading dashboard lucky number result: An exception occurred") + errorHandler.dispatch(it.error!!) + processHorizontalGroupData(error = it.error, forceRefresh = forceRefresh) + } + } + }.launch("dashboard_lucky_number") + } + + private fun loadMessages(forceRefresh: Boolean) { + flowWithResourceIn { + val student = studentRepository.getCurrentStudent(true) + val semester = semesterRepository.getCurrentSemester(student) + + messageRepository.getMessages(student, semester, MessageFolder.RECEIVED, forceRefresh) + }.onEach { + when (it.status) { + Status.LOADING -> { + Timber.i("Loading dashboard messages data started") + if (forceRefresh) return@onEach + val unreadMessagesCount = it.data?.count { message -> message.unread } + + processHorizontalGroupData( + unreadMessagesCount = unreadMessagesCount, + isLoading = true, + forceRefresh = forceRefresh + ) + } + Status.SUCCESS -> { + Timber.i("Loading dashboard messages result: Success") + val unreadMessagesCount = it.data?.count { message -> message.unread } + + processHorizontalGroupData( + unreadMessagesCount = unreadMessagesCount, + forceRefresh = forceRefresh + ) + } + Status.ERROR -> { + Timber.i("Loading dashboard messages result: An exception occurred") + errorHandler.dispatch(it.error!!) + processHorizontalGroupData(error = it.error, forceRefresh = forceRefresh) + } + } + }.launch("dashboard_messages") + } + + private fun loadAttendance(forceRefresh: Boolean) { + flowWithResourceIn { + val student = studentRepository.getCurrentStudent(true) + val semester = semesterRepository.getCurrentSemester(student) + + attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, forceRefresh) + }.onEach { + when (it.status) { + Status.LOADING -> { + Timber.i("Loading dashboard attendance data started") + if (forceRefresh) return@onEach + val attendancePercentage = it.data?.calculatePercentage() + + processHorizontalGroupData( + attendancePercentage = attendancePercentage, + isLoading = true, + forceRefresh = forceRefresh + ) + } + Status.SUCCESS -> { + Timber.i("Loading dashboard attendance result: Success") + val attendancePercentage = it.data?.calculatePercentage() + + processHorizontalGroupData( + attendancePercentage = attendancePercentage, + forceRefresh = forceRefresh + ) + } + Status.ERROR -> { + Timber.i("Loading dashboard attendance result: An exception occurred") + errorHandler.dispatch(it.error!!) + + processHorizontalGroupData(error = it.error, forceRefresh = forceRefresh) + } + } + }.launch("dashboard_attendance") + } + + private fun loadGrades(forceRefresh: Boolean) { + flowWithResourceIn { + val student = studentRepository.getCurrentStudent(true) val semester = semesterRepository.getCurrentSemester(student) gradeRepository.getGrades(student, semester, forceRefresh) - } - .mapResourceData { (details, _) -> - val filteredSubjectWithGrades = details - .filter { it.date >= LocalDate.now().minusDays(7) } - .groupBy { it.subject } - .mapValues { entry -> - entry.value - .take(5) - .sortedByDescending { it.date } - } - .toList() - .sortedByDescending { (_, grades) -> grades[0].date } - .toMap() + }.map { originalResource -> + val filteredSubjectWithGrades = originalResource.data?.first.orEmpty() + .filter { grade -> + grade.date.isAfter(LocalDate.now().minusDays(7)) + } + .groupBy { grade -> grade.subject } + .mapValues { entry -> + entry.value + .take(5) + .sortedBy { grade -> grade.date } + } + .toList() + .sortedBy { subjectWithGrades -> subjectWithGrades.second[0].date } + .toMap() - filteredSubjectWithGrades - } - .onEach { - when (it) { - is Resource.Loading -> { - Timber.i("Loading dashboard grades data started") - if (forceRefresh) return@onEach - updateData( - DashboardItem.Grades( - subjectWithGrades = it.dataOrNull, - gradeTheme = preferencesRepository.gradeColorTheme, - isLoading = true - ), forceRefresh - ) - - if (!it.dataOrNull.isNullOrEmpty()) { - firstLoadedItemList += DashboardItem.Type.GRADES - } - } - is Resource.Success -> { - Timber.i("Loading dashboard grades result: Success") - updateData( - DashboardItem.Grades( - subjectWithGrades = it.data, - gradeTheme = preferencesRepository.gradeColorTheme - ), - forceRefresh - ) - } - is Resource.Error -> { - Timber.i("Loading dashboard grades result: An exception occurred") - errorHandler.dispatch(it.error) - updateData(DashboardItem.Grades(error = it.error), forceRefresh) - } + Resource( + status = originalResource.status, + data = filteredSubjectWithGrades.takeIf { originalResource.data != null }, + error = originalResource.error + ) + }.onEach { + when (it.status) { + Status.LOADING -> { + Timber.i("Loading dashboard grades data started") + if (forceRefresh) return@onEach + updateData( + DashboardItem.Grades( + subjectWithGrades = it.data, + gradeTheme = preferencesRepository.gradeColorTheme, + isLoading = true + ), forceRefresh + ) + } + Status.SUCCESS -> { + Timber.i("Loading dashboard grades result: Success") + updateData( + DashboardItem.Grades( + subjectWithGrades = it.data, + gradeTheme = preferencesRepository.gradeColorTheme + ), forceRefresh + ) + } + Status.ERROR -> { + Timber.i("Loading dashboard grades result: An exception occurred") + errorHandler.dispatch(it.error!!) + updateData(DashboardItem.Grades(error = it.error), forceRefresh) } } - .launchWithUniqueRefreshJob("dashboard_grades", forceRefresh) + }.launch("dashboard_grades") } - private fun loadLessons(student: Student, forceRefresh: Boolean) { - flatResourceFlow { + private fun loadLessons(forceRefresh: Boolean) { + flowWithResourceIn { + val student = studentRepository.getCurrentStudent(true) val semester = semesterRepository.getCurrentSemester(student) val date = LocalDate.now().nextOrSameSchoolDay @@ -387,41 +392,30 @@ class DashboardPresenter @Inject constructor( end = date.plusDays(1), forceRefresh = forceRefresh ) - } - .onEach { - when (it) { - is Resource.Loading -> { - Timber.i("Loading dashboard lessons data started") - if (forceRefresh) return@onEach - updateData( - DashboardItem.Lessons(it.dataOrNull, isLoading = true), - forceRefresh - ) - if (!it.dataOrNull?.lessons.isNullOrEmpty()) { - 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) - updateData( - DashboardItem.Lessons(error = it.error), forceRefresh - ) - } + }.onEach { + when (it.status) { + Status.LOADING -> { + Timber.i("Loading dashboard lessons data started") + if (forceRefresh) return@onEach + updateData(DashboardItem.Lessons(it.data, isLoading = true), forceRefresh) + } + Status.SUCCESS -> { + Timber.i("Loading dashboard lessons result: Success") + updateData(DashboardItem.Lessons(it.data), forceRefresh) + } + Status.ERROR -> { + Timber.i("Loading dashboard lessons result: An exception occurred") + errorHandler.dispatch(it.error!!) + updateData(DashboardItem.Lessons(error = it.error), forceRefresh) } } - .launchWithUniqueRefreshJob("dashboard_lessons", forceRefresh) + }.launch("dashboard_lessons") } - private fun loadHomework(student: Student, forceRefresh: Boolean) { - flatResourceFlow { + private fun loadHomework(forceRefresh: Boolean) { + flowWithResourceIn { + val student = studentRepository.getCurrentStudent(true) val semester = semesterRepository.getCurrentSemester(student) val date = LocalDate.now().nextOrSameSchoolDay @@ -432,79 +426,70 @@ class DashboardPresenter @Inject constructor( end = date, forceRefresh = forceRefresh ) - } - .mapResourceData { homework -> - val currentDate = LocalDate.now() + }.map { homeworkResource -> + val currentDate = LocalDate.now() - val filteredHomework = homework.filter { - (it.date.isAfter(currentDate) || it.date == currentDate) && !it.isDone - }.sortedBy { it.date } - - filteredHomework + val filteredHomework = homeworkResource.data?.filter { + (it.date.isAfter(currentDate) || it.date == currentDate) && !it.isDone } - .onEach { - when (it) { - is Resource.Loading -> { - Timber.i("Loading dashboard homework data started") - if (forceRefresh) return@onEach - val data = it.dataOrNull.orEmpty() - updateData( - DashboardItem.Homework(data, isLoading = true), - forceRefresh - ) - if (data.isNotEmpty()) { - 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) - updateData(DashboardItem.Homework(error = it.error), forceRefresh) - } + homeworkResource.copy(data = filteredHomework) + }.onEach { + when (it.status) { + Status.LOADING -> { + Timber.i("Loading dashboard homework data started") + if (forceRefresh) return@onEach + updateData( + DashboardItem.Homework(it.data ?: emptyList(), isLoading = true), + forceRefresh + ) + } + Status.SUCCESS -> { + Timber.i("Loading dashboard homework result: Success") + updateData(DashboardItem.Homework(it.data ?: emptyList()), forceRefresh) + } + Status.ERROR -> { + Timber.i("Loading dashboard homework result: An exception occurred") + errorHandler.dispatch(it.error!!) + updateData(DashboardItem.Homework(error = it.error), forceRefresh) } } - .launchWithUniqueRefreshJob("dashboard_homework", forceRefresh) + }.launch("dashboard_homework") } - private fun loadSchoolAnnouncements(student: Student, forceRefresh: Boolean) { - flatResourceFlow { + private fun loadSchoolAnnouncements(forceRefresh: Boolean) { + flowWithResourceIn { + val student = studentRepository.getCurrentStudent(true) + schoolAnnouncementRepository.getSchoolAnnouncements(student, forceRefresh) - } - .onEach { - when (it) { - is Resource.Loading -> { - Timber.i("Loading dashboard announcements data started") - if (forceRefresh) return@onEach - updateData( - DashboardItem.Announcements(it.dataOrNull.orEmpty(), isLoading = true), - forceRefresh - ) - - if (!it.dataOrNull.isNullOrEmpty()) { - 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) - updateData(DashboardItem.Announcements(error = it.error), forceRefresh) - } + }.onEach { + when (it.status) { + Status.LOADING -> { + Timber.i("Loading dashboard announcements data started") + if (forceRefresh) return@onEach + updateData( + DashboardItem.Announcements( + it.data ?: emptyList(), + isLoading = true + ), forceRefresh + ) + } + Status.SUCCESS -> { + Timber.i("Loading dashboard announcements result: Success") + updateData(DashboardItem.Announcements(it.data ?: emptyList()), forceRefresh) + } + Status.ERROR -> { + Timber.i("Loading dashboard announcements result: An exception occurred") + errorHandler.dispatch(it.error!!) + updateData(DashboardItem.Announcements(error = it.error), forceRefresh) } } - .launchWithUniqueRefreshJob("dashboard_announcements", forceRefresh) + }.launch("dashboard_announcements") } - private fun loadExams(student: Student, forceRefresh: Boolean) { - flatResourceFlow { + private fun loadExams(forceRefresh: Boolean) { + flowWithResourceIn { + val student = studentRepository.getCurrentStudent(true) val semester = semesterRepository.getCurrentSemester(student) examRepository.getExams( @@ -514,289 +499,202 @@ class DashboardPresenter @Inject constructor( end = LocalDate.now().plusDays(7), forceRefresh = forceRefresh ) - } - .mapResourceData { exams -> exams.sortedBy { exam -> exam.date } } - .onEach { - when (it) { - is Resource.Loading -> { - Timber.i("Loading dashboard exams data started") - if (forceRefresh) return@onEach - updateData( - DashboardItem.Exams(it.dataOrNull.orEmpty(), isLoading = true), - forceRefresh - ) - - if (!it.dataOrNull.isNullOrEmpty()) { - 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) - updateData(DashboardItem.Exams(error = it.error), forceRefresh) - } + }.onEach { + when (it.status) { + Status.LOADING -> { + Timber.i("Loading dashboard exams data started") + if (forceRefresh) return@onEach + updateData( + DashboardItem.Exams(it.data.orEmpty(), isLoading = true), + forceRefresh + ) + } + Status.SUCCESS -> { + Timber.i("Loading dashboard exams result: Success") + updateData(DashboardItem.Exams(it.data ?: emptyList()), forceRefresh) + } + Status.ERROR -> { + Timber.i("Loading dashboard exams result: An exception occurred") + errorHandler.dispatch(it.error!!) + updateData(DashboardItem.Exams(error = it.error), forceRefresh) } } - .launchWithUniqueRefreshJob("dashboard_exams", forceRefresh) + }.launch("dashboard_exams") } - private fun loadConferences(student: Student, forceRefresh: Boolean) { - flatResourceFlow { + private fun loadConferences(forceRefresh: Boolean) { + flowWithResourceIn { + val student = studentRepository.getCurrentStudent(true) val semester = semesterRepository.getCurrentSemester(student) conferenceRepository.getConferences( student = student, semester = semester, forceRefresh = forceRefresh, - startDate = Instant.now(), + startDate = LocalDateTime.now() ) + }.onEach { + when (it.status) { + Status.LOADING -> { + Timber.i("Loading dashboard conferences data started") + if (forceRefresh) return@onEach + updateData( + DashboardItem.Conferences(it.data ?: emptyList(), isLoading = true), + forceRefresh + ) + } + Status.SUCCESS -> { + Timber.i("Loading dashboard conferences result: Success") + updateData(DashboardItem.Conferences(it.data ?: emptyList()), forceRefresh) + } + Status.ERROR -> { + Timber.i("Loading dashboard conferences result: An exception occurred") + errorHandler.dispatch(it.error!!) + updateData(DashboardItem.Conferences(error = it.error), forceRefresh) + } + } + }.launch("dashboard_conferences") + } + + private fun processHorizontalGroupData( + luckyNumber: Int? = null, + unreadMessagesCount: Int? = null, + attendancePercentage: Double? = null, + error: Throwable? = null, + isLoading: Boolean = false, + forceRefresh: Boolean + ) { + val isLuckyNumberToLoad = + dashboardTilesToLoad.any { it == DashboardItem.Tile.LUCKY_NUMBER } + val isMessagesToLoad = + dashboardTilesToLoad.any { it == DashboardItem.Tile.MESSAGES } + val isAttendanceToLoad = + dashboardTilesToLoad.any { it == DashboardItem.Tile.ATTENDANCE } + val isPushedToList = + dashboardItemLoadedList.any { it.type == DashboardItem.Type.HORIZONTAL_GROUP } + + if (error != null) { + updateData(DashboardItem.HorizontalGroup(error = error), forceRefresh) + return } - .onEach { - when (it) { - is Resource.Loading -> { - Timber.i("Loading dashboard conferences data started") - if (forceRefresh) return@onEach - updateData( - DashboardItem.Conferences(it.dataOrNull.orEmpty(), isLoading = true), - forceRefresh - ) - if (!it.dataOrNull.isNullOrEmpty()) { - 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) - updateData(DashboardItem.Conferences(error = it.error), forceRefresh) - } - } + if (isLoading) { + val horizontalGroup = + dashboardItemLoadedList.find { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup? + val updatedHorizontalGroup = + horizontalGroup?.copy(isLoading = true) ?: DashboardItem.HorizontalGroup(isLoading = true) + + updateData(updatedHorizontalGroup, forceRefresh) + } + + if (forceRefresh && !isPushedToList) { + updateData(DashboardItem.HorizontalGroup(), forceRefresh) + } + + val horizontalGroup = + dashboardItemLoadedList.single { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup + + when { + luckyNumber != null -> { + updateData(horizontalGroup.copy(luckyNumber = luckyNumber), forceRefresh) } - .launchWithUniqueRefreshJob("dashboard_conferences", forceRefresh) - } - - 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 + unreadMessagesCount != null -> { + updateData( + horizontalGroup.copy(unreadMessagesCount = unreadMessagesCount), + forceRefresh + ) } - .onEach { - when (it) { - is Resource.Loading -> { - Timber.i("Loading dashboard admin message data started") - if (forceRefresh) return@onEach - updateData(DashboardItem.AdminMessages(), forceRefresh) - } - is Resource.Success -> { - Timber.i("Loading dashboard admin message result: Success") - updateData( - dashboardItem = DashboardItem.AdminMessages(adminMessage = it.data), - forceRefresh = forceRefresh - ) - } - is Resource.Error -> { - Timber.i("Loading dashboard admin message result: An exception occurred") - Timber.e(it.error) - updateData( - dashboardItem = DashboardItem.AdminMessages( - adminMessage = null, - error = it.error - ), - forceRefresh = forceRefresh - ) - } - } + attendancePercentage != null -> { + updateData( + horizontalGroup.copy(attendancePercentage = attendancePercentage), + forceRefresh + ) } - .launchWithUniqueRefreshJob("dashboard_admin_messages", forceRefresh) - } + } - private fun loadAds(forceRefresh: Boolean) { - presenterScope.launch { - if (!forceRefresh) { - updateData(DashboardItem.Ads(), forceRefresh) - } + val isHorizontalGroupLoaded = dashboardItemLoadedList.any { + if (it !is DashboardItem.HorizontalGroup) return@any false - val dashboardAdItem = - runCatching { - DashboardItem.Ads(adsHelper.getDashboardTileAdBanner(view!!.tileWidth)) - } - .onFailure { Timber.e(it) } - .getOrElse { DashboardItem.Ads(error = it) } + val isLuckyNumberStateCorrect = (it.luckyNumber != null) == isLuckyNumberToLoad + val isMessagesStateCorrect = (it.unreadMessagesCount != null) == isMessagesToLoad + val isAttendanceStateCorrect = (it.attendancePercentage != null) == isAttendanceToLoad - updateData(dashboardAdItem, forceRefresh) + isLuckyNumberStateCorrect && isAttendanceStateCorrect && isMessagesStateCorrect + } + + if (isHorizontalGroupLoaded) { + val updatedHorizontalGroup = + dashboardItemLoadedList.single { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup + + updateData(updatedHorizontalGroup.copy(isLoading = false, error = null), forceRefresh) } } private fun updateData(dashboardItem: DashboardItem, forceRefresh: Boolean) { val isForceRefreshError = forceRefresh && dashboardItem.error != null - val isFirstRunDataLoadedError = - dashboardItem.type in firstLoadedItemList && dashboardItem.error != null + val dashboardItemsPosition = preferencesRepository.dashboardItemsPosition with(dashboardItemLoadedList) { - removeAll { it.type == dashboardItem.type && !isForceRefreshError && !isFirstRunDataLoadedError } - if (!isForceRefreshError && !isFirstRunDataLoadedError) add(dashboardItem) - } - - sortDashboardItems() - - if (dashboardItem is DashboardItem.AdminMessages) { - if (!dashboardItem.isDataLoaded) { - dashboardItemsToLoad = dashboardItemsToLoad - DashboardItem.Type.ADMIN_MESSAGE - dashboardTileLoadedList = dashboardTileLoadedList - DashboardItem.Tile.ADMIN_MESSAGE - - dashboardItemLoadedList.removeAll { it.type == DashboardItem.Type.ADMIN_MESSAGE } - } else { - dashboardItemsToLoad = dashboardItemsToLoad + DashboardItem.Type.ADMIN_MESSAGE - dashboardTileLoadedList = dashboardTileLoadedList + DashboardItem.Tile.ADMIN_MESSAGE - } - } - - if (dashboardItem is DashboardItem.Ads) { - if (!dashboardItem.isDataLoaded) { - dashboardItemsToLoad = dashboardItemsToLoad - DashboardItem.Type.ADS - dashboardTileLoadedList = dashboardTileLoadedList - DashboardItem.Tile.ADS - - dashboardItemLoadedList.removeAll { it.type == DashboardItem.Type.ADS } - } else { - dashboardItemsToLoad = dashboardItemsToLoad + DashboardItem.Type.ADS - dashboardTileLoadedList = dashboardTileLoadedList + DashboardItem.Tile.ADS - } + removeAll { it.type == dashboardItem.type && !isForceRefreshError } + if (!isForceRefreshError) add(dashboardItem) + sortBy { tile -> dashboardItemsToLoad.single { it == tile.type }.ordinal } } if (forceRefresh) { - updateForceRefreshData(dashboardItem) - } else { - updateNormalData() - } - } - - private fun updateNormalData() { - val isItemsLoaded = - dashboardItemsToLoad.all { type -> dashboardItemLoadedList.any { it.type == type } } - val isItemsDataLoaded = isItemsLoaded && dashboardItemLoadedList.all { - it.isDataLoaded || it.error != null - } - - if (isItemsDataLoaded) { - view?.run { - showProgress(false) - showErrorView(false) - showContent(true) - updateData(dashboardItemLoadedList.toList()) + with(dashboardItemRefreshLoadedList) { + removeAll { it.type == dashboardItem.type } + add(dashboardItem) } } - showErrorIfExists( - isItemsLoaded = isItemsLoaded, - itemsLoadedList = dashboardItemLoadedList, - forceRefresh = false - ) - } - - private fun updateForceRefreshData(dashboardItem: DashboardItem) { - val isNotLoadedAdminMessage = - dashboardItem is DashboardItem.AdminMessages && !dashboardItem.isDataLoaded - - with(dashboardItemRefreshLoadedList) { - removeAll { it.type == dashboardItem.type } - if (!isNotLoadedAdminMessage) add(dashboardItem) + dashboardItemLoadedList.sortBy { tile -> + dashboardItemsPosition?.getOrDefault( + tile.type, + tile.type.ordinal + 100 + ) ?: tile.type.ordinal } + val isItemsLoaded = + dashboardItemsToLoad.all { type -> dashboardItemLoadedList.any { it.type == type } } val isRefreshItemLoaded = dashboardItemsToLoad.all { type -> dashboardItemRefreshLoadedList.any { it.type == type } } + val isItemsDataLoaded = isItemsLoaded && dashboardItemLoadedList.all { + it.isDataLoaded || it.error != null + } val isRefreshItemsDataLoaded = isRefreshItemLoaded && dashboardItemRefreshLoadedList.all { it.isDataLoaded || it.error != null } if (isRefreshItemsDataLoaded) { - view?.run { - showRefresh(false) - showErrorView(false) - showContent(true) - updateData(dashboardItemLoadedList.toList()) + view?.showRefresh(false) + dashboardItemRefreshLoadedList.clear() + } + + view?.run { + if (!forceRefresh) { + showProgress(!isItemsDataLoaded) + showContent(isItemsDataLoaded) } + updateData(dashboardItemLoadedList.toList()) } - showErrorIfExists( - isItemsLoaded = isRefreshItemLoaded, - itemsLoadedList = dashboardItemRefreshLoadedList, - forceRefresh = true - ) + if (isItemsLoaded) { + val filteredItems = + dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT } + val isAccountItemError = + dashboardItemLoadedList.single { it.type == DashboardItem.Type.ACCOUNT }.error != null + val isGeneralError = + filteredItems.all { it.error != null } && filteredItems.isNotEmpty() || isAccountItemError - if (isRefreshItemsDataLoaded) dashboardItemRefreshLoadedList.clear() - } + val errorMessage = filteredItems.map { it.error?.stackTraceToString() }.toString() - private fun showErrorIfExists( - isItemsLoaded: Boolean, - itemsLoadedList: List, - forceRefresh: Boolean - ) { - val filteredItems = itemsLoadedList.filterNot { - it.type == DashboardItem.Type.ACCOUNT || it.type == DashboardItem.Type.ADMIN_MESSAGE - } - val isAccountItemError = - itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null - val isGeneralError = - filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError - val firstError = itemsLoadedList.firstNotNullOfOrNull { it.error } - - val filteredOriginalLoadedList = - dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT } - val wasAccountItemError = - dashboardItemLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null - val wasGeneralError = - filteredOriginalLoadedList.none { it.error == null } && filteredOriginalLoadedList.isNotEmpty() || wasAccountItemError - - if (isGeneralError && isItemsLoaded) { - lastError = requireNotNull(firstError) + lastError = Exception(errorMessage) view?.run { showProgress(false) - showRefresh(false) - if ((forceRefresh && wasGeneralError) || !forceRefresh) { - showContent(false) - showErrorView(true) - setErrorDetails(lastError) - } + showContent(!isGeneralError) + showErrorView(isGeneralError) } } } - - private fun sortDashboardItems() { - val dashboardItemsPosition = preferencesRepository.dashboardItemsPosition - - dashboardItemLoadedList.sortBy { tile -> - val defaultPosition = if (tile is DashboardItem.AdminMessages) { - -1 - } else { - tile.type.ordinal + 100 - } - - dashboardItemsPosition?.getOrDefault(tile.type, defaultPosition) ?: tile.type.ordinal - } - } - - private fun Flow>.launchWithUniqueRefreshJob(name: String, forceRefresh: Boolean) { - val jobName = if (forceRefresh) "$name-forceRefresh" else name - - if (forceRefresh) { - onEach { - if (it is Resource.Success) { - cancelJobs(jobName) - } - }.launch(jobName) - } else { - launch(jobName) - } - } -} +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt index 76788543..d5c5e5a7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt @@ -4,8 +4,6 @@ import io.github.wulkanowy.ui.base.BaseView interface DashboardView : BaseView { - val tileWidth: Int - fun initView() fun updateData(data: List) @@ -20,13 +18,9 @@ interface DashboardView : BaseView { fun showErrorView(show: Boolean) - fun setErrorDetails(error: Throwable) + fun setErrorDetails(message: String) fun resetView() fun popViewToRoot() - - fun openNotificationsCenterView() - - fun openInternetBrowser(url: String) -} +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt index 929e7264..1e11c874 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt @@ -1,7 +1,9 @@ package io.github.wulkanowy.ui.modules.debug.logviewer import android.content.Intent -import android.content.Intent.* +import android.content.Intent.EXTRA_EMAIL +import android.content.Intent.EXTRA_STREAM +import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION import android.os.Bundle import android.view.Menu import android.view.MenuInflater @@ -34,7 +36,6 @@ class LogViewerFragment : BaseFragment(R.layout.fragme fun newInstance() = LogViewerFragment() } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerPresenter.kt index 7adb56b8..4310ff87 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerPresenter.kt @@ -1,11 +1,11 @@ package io.github.wulkanowy.ui.modules.debug.logviewer -import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.LoggerRepository import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.flowWithResource import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -23,21 +23,19 @@ class LogViewerPresenter @Inject constructor( } fun onShareLogsSelected(): Boolean { - resourceFlow { loggerRepository.getLogFiles() } - .onEach { - when (it) { - is Resource.Loading -> Timber.d("Loading logs files started") - is Resource.Success -> { - Timber.i("Loading logs files result: ${it.data.joinToString { file -> file.name }}") - view?.shareLogs(it.data) - } - is Resource.Error -> { - Timber.i("Loading logs files result: An exception occurred") - errorHandler.dispatch(it.error) - } + flowWithResource { loggerRepository.getLogFiles() }.onEach { + when (it.status) { + Status.LOADING -> Timber.d("Loading logs files started") + Status.SUCCESS -> { + Timber.i("Loading logs files result: ${it.data!!.joinToString { file -> file.name }}") + view?.shareLogs(it.data) + } + Status.ERROR -> { + Timber.i("Loading logs files result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .launch("share") + }.launch("share") return true } @@ -46,20 +44,18 @@ class LogViewerPresenter @Inject constructor( } private fun loadLogFile() { - resourceFlow { loggerRepository.getLastLogLines() } - .onEach { - when (it) { - is Resource.Loading -> Timber.d("Loading last log file started") - is Resource.Success -> { - Timber.i("Loading last log file result: load ${it.data.size} lines") - view?.setLines(it.data) - } - is Resource.Error -> { - Timber.i("Loading last log file result: An exception occurred") - errorHandler.dispatch(it.error) - } + flowWithResource { loggerRepository.getLastLogLines() }.onEach { + when (it.status) { + Status.LOADING -> Timber.d("Loading last log file started") + Status.SUCCESS -> { + Timber.i("Loading last log file result: load ${it.data!!.size} lines") + view?.setLines(it.data) + } + Status.ERROR -> { + Timber.i("Loading last log file result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .launch("file") + }.launch("file") } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt index d0dfcd69..07468daa 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt @@ -3,8 +3,6 @@ package io.github.wulkanowy.ui.modules.debug.notification import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification -import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification import io.github.wulkanowy.services.sync.notifications.NewConferenceNotification import io.github.wulkanowy.services.sync.notifications.NewExamNotification import io.github.wulkanowy.services.sync.notifications.NewGradeNotification @@ -15,7 +13,6 @@ import io.github.wulkanowy.services.sync.notifications.NewNoteNotification import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.ui.modules.debug.notification.mock.debugAttendanceItems import io.github.wulkanowy.ui.modules.debug.notification.mock.debugConferenceItems import io.github.wulkanowy.ui.modules.debug.notification.mock.debugExamItems import io.github.wulkanowy.ui.modules.debug.notification.mock.debugGradeDetailsItems @@ -25,7 +22,6 @@ import io.github.wulkanowy.ui.modules.debug.notification.mock.debugLuckyNumber import io.github.wulkanowy.ui.modules.debug.notification.mock.debugMessageItems import io.github.wulkanowy.ui.modules.debug.notification.mock.debugNoteItems import io.github.wulkanowy.ui.modules.debug.notification.mock.debugSchoolAnnouncementItems -import io.github.wulkanowy.ui.modules.debug.notification.mock.debugTimetableItems import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -41,8 +37,6 @@ class NotificationDebugPresenter @Inject constructor( private val newNoteNotification: NewNoteNotification, private val newSchoolAnnouncementNotification: NewSchoolAnnouncementNotification, private val newLuckyNumberNotification: NewLuckyNumberNotification, - private val changeTimetableNotification: ChangeTimetableNotification, - private val newAttendanceNotification: NewAttendanceNotification, ) : BasePresenter(errorHandler, studentRepository) { private val items = listOf( @@ -70,12 +64,6 @@ class NotificationDebugPresenter @Inject constructor( NotificationDebugItem(R.string.note_title) { n -> withStudent { newNoteNotification.notify(debugNoteItems.take(n), it) } }, - NotificationDebugItem(R.string.attendance_title) { n -> - withStudent { newAttendanceNotification.notify(debugAttendanceItems.take(n), it) } - }, - NotificationDebugItem(R.string.timetable_title) { n -> - withStudent { changeTimetableNotification.notify(debugTimetableItems.take(n), it) } - }, NotificationDebugItem(R.string.school_announcement_title) { n -> withStudent { newSchoolAnnouncementNotification.notify(debugSchoolAnnouncementItems.take(n), it) @@ -99,8 +87,8 @@ class NotificationDebugPresenter @Inject constructor( } } - private fun withStudent(block: suspend (Student) -> Unit) { - presenterScope.launch { + private fun withStudent(block: (Student) -> Unit) { + launch { block(studentRepository.getCurrentStudent(false)) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/attendance.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/attendance.kt deleted file mode 100644 index 042cf07e..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/attendance.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.ui.modules.debug.notification.mock - -import io.github.wulkanowy.data.db.entities.Attendance -import java.time.LocalDate - -val debugAttendanceItems = listOf( - generateAttendance("Matematyka", "PRESENCE"), - generateAttendance("Język angielski", "UNEXCUSED_LATENESS"), - generateAttendance("Geografia", "ABSENCE_UNEXCUSED"), - generateAttendance("Sieci komputerowe", "ABSENCE_EXCUSED"), - generateAttendance("Systemy operacyjne", "EXCUSED_LATENESS"), - generateAttendance("Język niemiecki", "ABSENCE_UNEXCUSED"), - generateAttendance("Biologia", "ABSENCE_UNEXCUSED"), - generateAttendance("Chemia", "ABSENCE_EXCUSED"), - generateAttendance("Fizyka", "ABSENCE_UNEXCUSED"), - generateAttendance("Matematyka", "ABSENCE_EXCUSED"), -) - -private fun generateAttendance(subject: String, name: String) = Attendance( - subject = subject, - studentId = 0, - diaryId = 0, - date = LocalDate.now(), - timeId = 0, - number = 1, - name = name, - presence = false, - absence = false, - exemption = false, - lateness = false, - excused = false, - deleted = false, - excusable = false, - excuseStatus = "" -) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/conference.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/conference.kt index 625ff4c9..40af6bfb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/conference.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/conference.kt @@ -1,8 +1,7 @@ package io.github.wulkanowy.ui.modules.debug.notification.mock import io.github.wulkanowy.data.db.entities.Conference -import java.time.Duration -import java.time.Instant +import java.time.LocalDateTime val debugConferenceItems = listOf( generateConference( @@ -54,6 +53,6 @@ private fun generateConference(title: String, subject: String) = Conference( diaryId = 0, agenda = "", conferenceId = 0, - date = Instant.now().plus(Duration.ofMinutes(10)), + date = LocalDateTime.now().plusMinutes(10), presentOnConference = "", ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDetails.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDetails.kt index 77b60188..f9c481e3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDetails.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDetails.kt @@ -5,7 +5,7 @@ import java.time.LocalDate val debugGradeDetailsItems = listOf( generateGrade("Matematyka", "+"), - generateGrade("Matematyka", "120", comment = "%"), + generateGrade("Matematyka", "2="), generateGrade("Fizyka", "-"), generateGrade("Geografia", "4+"), generateGrade("Sieci komputerowe", "1"), @@ -17,14 +17,14 @@ val debugGradeDetailsItems = listOf( generateGrade("Wychowanie fizyczne", "5"), ) -private fun generateGrade(subject: String, entry: String, comment: String = "") = Grade( +private fun generateGrade(subject: String, entry: String) = Grade( subject = subject, entry = entry, semesterId = 0, studentId = 0, value = 0.0, modifier = 0.0, - comment = comment, + comment = "", color = "", gradeSymbol = "", description = "", diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt index 27d8613a..f506d2f6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt @@ -1,7 +1,7 @@ package io.github.wulkanowy.ui.modules.debug.notification.mock import io.github.wulkanowy.data.db.entities.Message -import java.time.Instant +import java.time.LocalDateTime val debugMessageItems = listOf( generateMessage("Kowalski Jan", "Tytuł"), @@ -17,16 +17,16 @@ val debugMessageItems = listOf( ) private fun generateMessage(sender: String, subject: String) = Message( + sender = sender, subject = subject, - messageId = 123, - email = "", - date = Instant.now(), + studentId = 0, + realId = 0, + messageId = 0, + senderId = 0, + recipient = "", + date = LocalDateTime.now(), folderId = 0, unread = true, - readBy = 2, - unreadBy = 2, - hasAttachments = false, - messageGlobalKey = "", - correspondents = sender, - mailboxKey = "", + removed = false, + hasAttachments = false ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt index e2dc5cd8..42524e6e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt @@ -4,13 +4,13 @@ import io.github.wulkanowy.data.db.entities.SchoolAnnouncement import java.time.LocalDate val debugSchoolAnnouncementItems = listOf( - generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych
03.05.2021 – poniedziałek"), + generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych\n03.05.2021 - poniedziałek"), generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do noszenia maseczek"), generateAnnouncement("Święto szkoły", "W najbliższych dniach obchodzimy święto szkoły, podczas którego..."), generateAnnouncement("Rocznica odzyskania przez szkołę sztandaru", "Juz niedługo, bo za tydzień, a dokładnie za 8 dni..."), generateAnnouncement("Ogłoszenie w sprawie otwarcia stołówki", "Wszyscy uczniowie zainteresowani obiadami w szkole..."), generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie pilnym"), - generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych
21.06.2021 – poniedziałek"), + generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych\n21.06.2021 - poniedziałek"), generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do zdjęcia maseczek"), generateAnnouncement("Święto państwowe", "W najbliższych dniach obchodzimy święto państwowe, podczas którego..."), generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie wolnym"), @@ -19,6 +19,6 @@ val debugSchoolAnnouncementItems = listOf( private fun generateAnnouncement(subject: String, content: String) = SchoolAnnouncement( subject = subject, content = content, - userLoginId = 0, + studentId = 0, date = LocalDate.now() ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt deleted file mode 100644 index ff968654..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.ui.modules.debug.notification.mock - -import io.github.wulkanowy.data.db.entities.Timetable -import java.time.Duration -import java.time.Instant -import java.time.LocalDate -import kotlin.random.Random - -val debugTimetableItems = listOf( - generateTimetable("Matematyka", "12", "01"), - generateTimetable("Język angielski", "23", "12"), - generateTimetable("Geografia", "34", "23"), - generateTimetable("Sieci komputerowe", "45", "34"), - generateTimetable("Systemy operacyjne", "56", "45"), - generateTimetable("Język niemiecki", "67", "56"), - generateTimetable("Biologia", "78", "67"), - generateTimetable("Chemia", "89", "78"), - generateTimetable("Fizyka", "90", "89"), - generateTimetable("Matematyka", "01", "90"), -) - -private fun generateTimetable(subject: String, room: String, roomOld: String) = Timetable( - subject = subject, - studentId = 0, - diaryId = 0, - date = LocalDate.now().minusDays(Random.nextLong(0, 8)), - number = 1, - start = Instant.now().plus(Duration.ofHours(1)), - end = Instant.now(), - subjectOld = "", - group = "", - room = room, - roomOld = roomOld, - teacher = "Wtorkowska Renata", - teacherOld = "", - info = "", - isStudentPlan = true, - changes = true, - canceled = true -) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt index d452d74a..3f815a2c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt @@ -1,22 +1,18 @@ package io.github.wulkanowy.ui.modules.exam -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.databinding.DialogExamBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.openCalendarEventAdd -import io.github.wulkanowy.utils.serializable +import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.toFormattedString -import java.time.LocalTime -@AndroidEntryPoint -class ExamDialog : BaseDialogFragment() { +class ExamDialog : DialogFragment() { + + private var binding: DialogExamBinding by lifecycleAwareVariable() private lateinit var exam: Exam @@ -25,20 +21,23 @@ class ExamDialog : BaseDialogFragment() { private const val ARGUMENT_KEY = "Item" fun newInstance(exam: Exam) = ExamDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to exam) + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - exam = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + exam = getSerializable(ARGUMENT_KEY) as Exam + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogExamBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogExamBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -47,21 +46,10 @@ class ExamDialog : BaseDialogFragment() { examDialogSubjectValue.text = exam.subject examDialogTypeValue.text = exam.type examDialogTeacherValue.text = exam.teacher - examDialogEntryDateValue.text = exam.entryDate.toFormattedString() - examDialogDeadlineDateValue.text = exam.date.toFormattedString() - examDialogDescriptionValue.text = exam.description.ifBlank { - getString(R.string.all_no_data) - } + examDialogDateValue.text = exam.entryDate.toFormattedString() + examDialogDescriptionValue.text = exam.description examDialogClose.setOnClickListener { dismiss() } - examDialogAddToCalendar.setOnClickListener { - requireContext().openCalendarEventAdd( - title = "${exam.subject} - ${exam.type}", - description = exam.description, - start = exam.date.atTime(LocalTime.of(8, 0)), - end = exam.date.atTime(LocalTime.of(8, 45)), - ) - } } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt index 0123e234..fb7939bc 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt @@ -2,7 +2,9 @@ package io.github.wulkanowy.ui.modules.exam import android.os.Bundle import android.view.View -import android.view.View.* +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -18,7 +20,7 @@ import javax.inject.Inject @AndroidEntryPoint class ExamFragment : BaseFragment(R.layout.fragment_exam), ExamView, - MainView.TitledView, MainView.MainChildView { + MainView.TitledView { @Inject lateinit var presenter: ExamPresenter @@ -62,7 +64,7 @@ class ExamFragment : BaseFragment(R.layout.fragment_exam), examPreviousButton.setOnClickListener { presenter.onPreviousWeek() } examNextButton.setOnClickListener { presenter.onNextWeek() } - examNavContainer.elevation = requireContext().dpToPx(3f) + examNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -124,14 +126,6 @@ class ExamFragment : BaseFragment(R.layout.fragment_exam), (activity as? MainActivity)?.showDialogFragment(ExamDialog.newInstance(exam)) } - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onViewReselected() - } - - override fun resetView() { - binding.examRecycler.smoothScrollToPosition(0) - } - override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt index 85814072..582641fc 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt @@ -1,13 +1,21 @@ package io.github.wulkanowy.ui.modules.exam -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.repositories.ExamRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEach @@ -78,57 +86,61 @@ class ExamPresenter @Inject constructor( 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") + }.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) { - flatResourceFlow { + Timber.i("Loading exam data started") + + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) - examRepository.getExams( - student = student, - semester = semester, - start = currentDate.monday, - end = currentDate.sunday, - forceRefresh = forceRefresh - ) - } - .logResourceStatus("load exam data") - .mapResourceData { createExamItems(it) } - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) - updateData(it) + examRepository.getExams(student, semester, currentDate.monday, currentDate.sunday, forceRefresh) + }.onEach { + when (it.status) { + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(createExamItems(it.data)) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading exam result: Success") + view?.apply { + updateData(createExamItems(it.data!!)) + showEmpty(it.data.isEmpty()) + showErrorView(false) + showContent(it.data.isNotEmpty()) + } + analytics.logEvent( + "load_data", + "type" to "exam", + "items" to it.data!!.size + ) + } + Status.ERROR -> { + Timber.i("Loading exam result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "exam", - "items" to it.size - ) + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showProgress(false) - showRefresh(false) - } - } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -169,23 +181,8 @@ class ExamPresenter @Inject constructor( view?.apply { showPreButton(!currentDate.minusDays(7).isHolidays) showNextButton(!currentDate.plusDays(7).isHolidays) - updateNavigationWeek( - "${currentDate.monday.toFormattedString("dd.MM")} - " + - currentDate.sunday.toFormattedString("dd.MM") - ) - } - } - - fun onViewReselected() { - Timber.i("Exam view is reselected") - - baseDate = now().nextOrSameSchoolDay - - if (currentDate != baseDate) { - reloadView(baseDate) - loadData() - } else if (view?.isViewEmpty == false) { - view?.resetView() + updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " + + currentDate.sunday.toFormattedString("dd.MM")) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt index 677fac40..45b9e788 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt @@ -34,6 +34,4 @@ interface ExamView : BaseView { fun showPreButton(show: Boolean) fun showExamDialog(exam: Exam) - - fun resetView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt index 2d63aae4..4a304972 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt @@ -1,6 +1,7 @@ package io.github.wulkanowy.ui.modules.grade -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.Semester @@ -9,148 +10,128 @@ import io.github.wulkanowy.data.repositories.GradeRepository import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.* +import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.ALL_YEAR +import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.BOTH_SEMESTERS +import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.ONE_SEMESTER import io.github.wulkanowy.utils.calcAverage import io.github.wulkanowy.utils.changeModifier -import kotlinx.coroutines.ExperimentalCoroutinesApi +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map import javax.inject.Inject -@OptIn(ExperimentalCoroutinesApi::class) +@OptIn(FlowPreview::class) class GradeAverageProvider @Inject constructor( private val semesterRepository: SemesterRepository, private val gradeRepository: GradeRepository, private val preferencesRepository: PreferencesRepository ) { - private data class AverageCalcParams( - val gradeAverageMode: GradeAverageMode, - val forceAverageCalc: Boolean, - val isOptionalArithmeticAverage: Boolean, - val plusModifier: Double, - val minusModifier: Double, - ) + private val plusModifier get() = preferencesRepository.gradePlusModifier - fun getGradesDetailsWithAverage( - student: Student, - semesterId: Int, - forceRefresh: Boolean - ): Flow>> = combine( - flow = preferencesRepository.gradeAverageModeFlow, - flow2 = preferencesRepository.gradeAverageForceCalcFlow, - flow3 = preferencesRepository.isOptionalArithmeticAverageFlow, - flow4 = preferencesRepository.gradePlusModifierFlow, - flow5 = preferencesRepository.gradeMinusModifierFlow, - ) { gradeAverageMode, forceAverageCalc, isOptionalArithmeticAverage, plusModifier, minusModifier -> - AverageCalcParams( - gradeAverageMode = gradeAverageMode, - forceAverageCalc = forceAverageCalc, - isOptionalArithmeticAverage = isOptionalArithmeticAverage, - plusModifier = plusModifier, - minusModifier = minusModifier, - ) - }.flatMapLatest { params -> - flatResourceFlow { + private val minusModifier get() = preferencesRepository.gradeMinusModifier + + private val isOptionalArithmeticAverage get() = preferencesRepository.isOptionalArithmeticAverage + + fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) = + flowWithResourceIn { val semesters = semesterRepository.getSemesters(student) - when (params.gradeAverageMode) { + + when (preferencesRepository.gradeAverageMode) { ONE_SEMESTER -> getGradeSubjects( student = student, semester = semesters.single { it.semesterId == semesterId }, - forceRefresh = forceRefresh, - params = params, + forceRefresh = forceRefresh ) BOTH_SEMESTERS -> calculateCombinedAverage( student = student, semesters = semesters, semesterId = semesterId, forceRefresh = forceRefresh, - config = params, + averageMode = BOTH_SEMESTERS ) ALL_YEAR -> calculateCombinedAverage( student = student, semesters = semesters, semesterId = semesterId, forceRefresh = forceRefresh, - config = params, + averageMode = ALL_YEAR ) } - } - }.distinctUntilChanged() + }.distinctUntilChanged() private fun calculateCombinedAverage( student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean, - config: AverageCalcParams, + averageMode: GradeAverageMode ): Flow>> { + val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc val selectedSemester = semesters.single { it.semesterId == semesterId } val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 } val selectedSemesterGradeSubjects = - getGradeSubjects(student, selectedSemester, forceRefresh, config) + getGradeSubjects(student, selectedSemester, forceRefresh) if (selectedSemester == firstSemester) return selectedSemesterGradeSubjects - val firstSemesterGradeSubjects = - getGradeSubjects(student, firstSemester, forceRefresh, config) + val firstSemesterGradeSubjects = getGradeSubjects(student, firstSemester, forceRefresh) return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject -> - if (firstSemesterGradeSubject.errorOrNull != null) { + if (firstSemesterGradeSubject.status == Status.ERROR) { return@combine firstSemesterGradeSubject } val isAnyVulcanAverageInFirstSemester = - firstSemesterGradeSubject.dataOrNull.orEmpty().any { it.isVulcanAverage } + firstSemesterGradeSubject.data.orEmpty().any { it.isVulcanAverage } val isAnyVulcanAverageInSecondSemester = - secondSemesterGradeSubject.dataOrNull.orEmpty().any { it.isVulcanAverage } + secondSemesterGradeSubject.data.orEmpty().any { it.isVulcanAverage } - val updatedData = secondSemesterGradeSubject.dataOrNull?.map { secondSemesterSubject -> - val firstSemesterSubject = firstSemesterGradeSubject.dataOrNull.orEmpty() + val updatedData = secondSemesterGradeSubject.data?.map { secondSemesterSubject -> + val firstSemesterSubject = firstSemesterGradeSubject.data.orEmpty() .singleOrNull { it.subject == secondSemesterSubject.subject } - val updatedAverage = if (config.gradeAverageMode == ALL_YEAR) { + val updatedAverage = if (averageMode == ALL_YEAR) { calculateAllYearAverage( student = student, isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester, + isGradeAverageForceCalc = isGradeAverageForceCalc, secondSemesterSubject = secondSemesterSubject, - firstSemesterSubject = firstSemesterSubject, - config = config, + firstSemesterSubject = firstSemesterSubject ) } else { calculateBothSemestersAverage( student = student, isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester, + isGradeAverageForceCalc = isGradeAverageForceCalc, secondSemesterSubject = secondSemesterSubject, - firstSemesterSubject = firstSemesterSubject, - config = config + firstSemesterSubject = firstSemesterSubject ) } secondSemesterSubject.copy(average = updatedAverage) } - secondSemesterGradeSubject.mapData { updatedData!! } + secondSemesterGradeSubject.copy(data = updatedData) } } private fun calculateAllYearAverage( student: Student, isAnyVulcanAverage: Boolean, + isGradeAverageForceCalc: Boolean, secondSemesterSubject: GradeSubject, - firstSemesterSubject: GradeSubject?, - config: AverageCalcParams, - ) = if (!isAnyVulcanAverage || config.forceAverageCalc) { - val updatedSecondSemesterGrades = secondSemesterSubject.grades - .updateModifiers(student, config) - val updatedFirstSemesterGrades = firstSemesterSubject?.grades - ?.updateModifiers(student, config).orEmpty() + firstSemesterSubject: GradeSubject? + ) = if (!isAnyVulcanAverage || isGradeAverageForceCalc) { + val updatedSecondSemesterGrades = + secondSemesterSubject.grades.updateModifiers(student) + val updatedFirstSemesterGrades = + firstSemesterSubject?.grades?.updateModifiers(student).orEmpty() - (updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage( - config.isOptionalArithmeticAverage - ) + (updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(isOptionalArithmeticAverage) } else { secondSemesterSubject.average } @@ -158,54 +139,48 @@ class GradeAverageProvider @Inject constructor( private fun calculateBothSemestersAverage( student: Student, isAnyVulcanAverage: Boolean, + isGradeAverageForceCalc: Boolean, secondSemesterSubject: GradeSubject, - firstSemesterSubject: GradeSubject?, - config: AverageCalcParams, + firstSemesterSubject: GradeSubject? ): Double { - return if (!isAnyVulcanAverage || config.forceAverageCalc) { - val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1 - val secondSemesterAverage = secondSemesterSubject.grades - .updateModifiers(student, config) - .calcAverage(config.isOptionalArithmeticAverage) - val firstSemesterAverage = firstSemesterSubject?.grades - ?.updateModifiers(student, config) - ?.calcAverage(config.isOptionalArithmeticAverage) ?: secondSemesterAverage + val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1 + + return if (!isAnyVulcanAverage || isGradeAverageForceCalc) { + val secondSemesterAverage = + secondSemesterSubject.grades.updateModifiers(student).calcAverage(isOptionalArithmeticAverage) + val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student) + ?.calcAverage(isOptionalArithmeticAverage) ?: secondSemesterAverage (secondSemesterAverage + firstSemesterAverage) / divider } else { - val divider = if (secondSemesterSubject.average > 0) 2 else 1 - - secondSemesterSubject.average.plus( - (firstSemesterSubject?.average ?: secondSemesterSubject.average) - ) / divider + (secondSemesterSubject.average + (firstSemesterSubject?.average ?: secondSemesterSubject.average)) / divider } } private fun getGradeSubjects( student: Student, semester: Semester, - forceRefresh: Boolean, - params: AverageCalcParams, + forceRefresh: Boolean ): Flow>> { - return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh) - .mapResourceData { res -> - val (details, summaries) = res - val isAnyAverage = summaries.any { it.average != .0 } - val allGrades = details.groupBy { it.subject } + val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc - val items = summaries.emulateEmptySummaries( + return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh) + .map { res -> + val (details, summaries) = res.data ?: null to null + val isAnyAverage = summaries.orEmpty().any { it.average != .0 } + val allGrades = details.orEmpty().groupBy { it.subject } + + val items = summaries?.emulateEmptySummaries( student = student, semester = semester, grades = allGrades.toList(), - calcAverage = isAnyAverage, - params = params, - ).map { summary -> + calcAverage = isAnyAverage + )?.map { summary -> val grades = allGrades[summary.subject].orEmpty() GradeSubject( subject = summary.subject, - average = if (!isAnyAverage || params.forceAverageCalc) { - grades.updateModifiers(student, params) - .calcAverage(params.isOptionalArithmeticAverage) + average = if (!isAnyAverage || isGradeAverageForceCalc) { + grades.updateModifiers(student).calcAverage(isOptionalArithmeticAverage) } else summary.average, points = summary.pointsSum, summary = summary, @@ -214,7 +189,7 @@ class GradeAverageProvider @Inject constructor( ) } - items + Resource(res.status, items, res.error) } } @@ -222,8 +197,7 @@ class GradeAverageProvider @Inject constructor( student: Student, semester: Semester, grades: List>>, - calcAverage: Boolean, - params: AverageCalcParams, + calcAverage: Boolean ): List { if (isNotEmpty() && size > grades.size) return this @@ -239,16 +213,14 @@ class GradeAverageProvider @Inject constructor( proposedPoints = "", finalPoints = "", pointsSum = "", - average = if (calcAverage) details.updateModifiers(student, params) - .calcAverage(params.isOptionalArithmeticAverage) else .0 + average = if (calcAverage) details.updateModifiers(student).calcAverage(isOptionalArithmeticAverage) else .0 ) } } - private fun List.updateModifiers( - student: Student, - params: AverageCalcParams, - ): List = if (student.loginMode == Sdk.Mode.SCRAPPER.name) { - map { it.changeModifier(params.plusModifier, params.minusModifier) } - } else this + private fun List.updateModifiers(student: Student): List { + return if (student.loginMode == Sdk.Mode.SCRAPPER.name) { + map { it.changeModifier(plusModifier, minusModifier) } + } else this + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt index 7ce07eb6..b3ef3037 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt @@ -8,8 +8,6 @@ import android.view.View import android.view.View.INVISIBLE import android.view.View.VISIBLE import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Semester @@ -31,13 +29,7 @@ class GradeFragment : BaseFragment(R.layout.fragment_grade @Inject lateinit var presenter: GradePresenter - private val pagerAdapter by lazy { - BaseFragmentPagerAdapter( - fragmentManager = childFragmentManager, - pagesCount = 3, - lifecycle = lifecycle, - ) - } + private val pagerAdapter by lazy { BaseFragmentPagerAdapter(childFragmentManager) } private var semesterSwitchMenu: MenuItem? = null @@ -52,7 +44,6 @@ class GradeFragment : BaseFragment(R.layout.fragment_grade override val currentPageIndex get() = binding.gradeViewPager.currentItem - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -71,35 +62,28 @@ class GradeFragment : BaseFragment(R.layout.fragment_grade } override fun initView() { + with(pagerAdapter) { + containerId = binding.gradeViewPager.id + addFragmentsWithTitle( + mapOf( + GradeDetailsFragment.newInstance() to getString(R.string.all_details), + GradeSummaryFragment.newInstance() to getString(R.string.grade_menu_summary), + GradeStatisticsFragment.newInstance() to getString(R.string.grade_menu_statistics) + ) + ) + } + with(binding.gradeViewPager) { adapter = pagerAdapter offscreenPageLimit = 3 setOnSelectPageListener(presenter::onPageSelected) } - with(pagerAdapter) { - containerId = binding.gradeViewPager.id - titleFactory = { - when (it) { - 0 -> getString(R.string.all_details) - 1 -> getString(R.string.grade_menu_summary) - 2 -> getString(R.string.grade_menu_statistics) - else -> throw IllegalStateException() - } - } - itemFactory = { - when (it) { - 0 -> GradeDetailsFragment.newInstance() - 1 -> GradeSummaryFragment.newInstance() - 2 -> GradeStatisticsFragment.newInstance() - else -> throw IllegalStateException() - } - } - TabLayoutMediator(binding.gradeTabLayout, binding.gradeViewPager, this).attach() + with(binding.gradeTabLayout) { + setupWithViewPager(binding.gradeViewPager) + setElevationCompat(context.dpToPx(4f)) } - binding.gradeTabLayout.elevation = requireContext().dpToPx(4f) - with(binding) { gradeErrorRetry.setOnClickListener { presenter.onRetry() } gradeErrorDetails.setOnClickListener { presenter.onDetailsClick() } @@ -142,7 +126,7 @@ class GradeFragment : BaseFragment(R.layout.fragment_grade val choices = semesters.map { getString(R.string.grade_semester, it.semesterName) } .toTypedArray() - MaterialAlertDialogBuilder(requireContext()) + AlertDialog.Builder(requireContext()) .setSingleChoiceItems(choices, selectedIndex) { dialog, which -> presenter.onSemesterSelected(which) dialog.dismiss() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt index 0ae6521c..504c730d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt @@ -1,16 +1,16 @@ package io.github.wulkanowy.ui.modules.grade +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.onResourceData -import io.github.wulkanowy.data.onResourceError import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.getCurrentOrLast +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -99,26 +99,33 @@ class GradePresenter @Inject constructor( } private fun loadData() { - resourceFlow { + flowWithResource { val student = studentRepository.getCurrentStudent() + delay(200) semesterRepository.getSemesters(student, refreshOnNoCurrent = true) - } - .logResourceStatus("load grade data") - .onResourceData { - val current = it.getCurrentOrLast() - selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex - schoolYear = current.schoolYear - semesters = it.filter { semester -> semester.diaryId == current.diaryId } - view?.setCurrentSemesterName(current.semesterName, schoolYear) - view?.run { - Timber.i("Loading grade data: Attempt load index $currentPageIndex") - loadChild(currentPageIndex) - showErrorView(false) - showSemesterSwitch(true) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading grade data started") + Status.SUCCESS -> { + val current = it.data!!.getCurrentOrLast() + selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex + schoolYear = current.schoolYear + semesters = it.data.filter { semester -> semester.diaryId == current.diaryId } + view?.setCurrentSemesterName(current.semesterName, schoolYear) + + view?.run { + Timber.i("Loading grade result: Attempt load index $currentPageIndex") + loadChild(currentPageIndex) + showErrorView(false) + showSemesterSwitch(true) + } + } + Status.ERROR -> { + Timber.i("Loading grade result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSortingMode.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSortingMode.kt new file mode 100644 index 00000000..1e6b26e8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeSortingMode.kt @@ -0,0 +1,10 @@ +package io.github.wulkanowy.ui.modules.grade + +enum class GradeSortingMode(val value: String) { + ALPHABETIC("alphabetic"), + DATE("date"); + + companion object { + fun getByValue(value: String) = values().firstOrNull { it.value == value } ?: ALPHABETIC + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt index 15b5db03..01631140 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt @@ -1,27 +1,21 @@ package io.github.wulkanowy.ui.modules.grade.details import android.annotation.SuppressLint -import android.content.res.ColorStateList import android.content.res.Resources import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.view.isVisible import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.NO_POSITION import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.enums.GradeColorTheme -import io.github.wulkanowy.data.enums.GradeExpandMode import io.github.wulkanowy.databinding.HeaderGradeDetailsBinding import io.github.wulkanowy.databinding.ItemGradeDetailsBinding import io.github.wulkanowy.ui.base.BaseExpandableAdapter import io.github.wulkanowy.utils.getBackgroundColor -import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.toFormattedString import timber.log.Timber -import java.util.* import javax.inject.Inject class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter() { @@ -30,20 +24,19 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter() - private val expandedPositions = BitSet(items.size) + private var expandedPosition = NO_POSITION - private var expandMode = GradeExpandMode.ONE + private var isExpandable = false var onClickListener: (Grade, position: Int) -> Unit = { _, _ -> } - lateinit var gradeColorTheme: GradeColorTheme + var colorTheme = "" - fun setDataItems(data: List, expandMode: GradeExpandMode = this.expandMode) { + fun setDataItems(data: List, isExpanded: Boolean = isExpandable) { headers = data.filter { it.viewType == ViewType.HEADER }.toMutableList() - items = - (if (expandMode != GradeExpandMode.ALWAYS_EXPANDED) headers else data).toMutableList() - this.expandMode = expandMode - expandedPositions.clear() + items = if (isExpanded) headers else data.toMutableList() + isExpandable = isExpanded + expandedPosition = NO_POSITION } fun updateDetailsItem(position: Int, grade: Grade) { @@ -55,7 +48,7 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter 1) { - Timber.e("Header with subject $subject found ${candidates.size} times! Expanded: $expandedPositions. Items: $candidates") + Timber.e("Header with subject $subject found ${candidates.size} times! Expanded: $expandedPosition. Items: $candidates") } return candidates.first() @@ -71,9 +64,9 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter HeaderViewHolder( - HeaderGradeDetailsBinding.inflate(inflater, parent, false) - ) - ViewType.ITEM.id -> ItemViewHolder( - ItemGradeDetailsBinding.inflate(inflater, parent, false) - ) + ViewType.HEADER.id -> HeaderViewHolder(HeaderGradeDetailsBinding.inflate(inflater, parent, false)) + ViewType.ITEM.id -> ItemViewHolder(ItemGradeDetailsBinding.inflate(inflater, parent, false)) else -> throw IllegalStateException() } } @@ -117,121 +106,65 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter 0 + gradeHeaderPointsSum.text = root.context.getString(R.string.grade_points_sum, header.pointsSum) + gradeHeaderPointsSum.visibility = if (!header.pointsSum.isNullOrEmpty()) View.VISIBLE else View.GONE + gradeHeaderNumber.text = root.context.resources.getQuantityString(R.plurals.grade_number_item, header.grades.size, header.grades.size) + gradeHeaderNote.visibility = if (header.newGrades > 0) View.VISIBLE else View.GONE + if (header.newGrades > 0) gradeHeaderNote.text = header.newGrades.toString(10) - if (header.newGrades > 0) { - gradeHeaderNote.text = header.newGrades.toString() - } - - gradeHeaderContainer.isEnabled = expandMode != GradeExpandMode.ALWAYS_EXPANDED + gradeHeaderContainer.isEnabled = isExpandable gradeHeaderContainer.setOnClickListener { - expandGradeHeader(headerPosition, header, holder) - } - } - } + expandedPosition = if (expandedPosition == adapterPosition) -1 else adapterPosition - private fun expandGradeHeader( - headerPosition: Int, - header: GradeDetailsHeader, - holder: HeaderViewHolder - ) { - if (expandMode == GradeExpandMode.ONE) { - val isHeaderExpanded = expandedPositions[headerPosition] - - expandedPositions.clear() - - if (!isHeaderExpanded) { - val updatedItemList = headers.toMutableList() - .apply { addAll(headerPosition + 1, header.grades) } - - expandedPositions.set(headerPosition) - refreshList(updatedItemList) - scrollToHeaderWithSubItems(headerPosition, header.grades.size) - } else { - refreshList(headers.toMutableList()) - } - } else if (expandMode == GradeExpandMode.UNLIMITED) { - val headerAdapterPosition = holder.bindingAdapterPosition - val isHeaderExpanded = expandedPositions[headerPosition] - - expandedPositions.flip(headerPosition) - - if (!isHeaderExpanded) { - val updatedList = items.toMutableList() - .apply { addAll(headerAdapterPosition + 1, header.grades) } - - refreshList(updatedList) - scrollToHeaderWithSubItems(headerAdapterPosition, header.grades.size) - } else { - val startPosition = headerAdapterPosition + 1 - val updatedList = items.toMutableList() - .apply { - subList(startPosition, startPosition + header.grades.size).clear() - } - - refreshList(updatedList) - } - } - } - - @SuppressLint("SetTextI18n") - private fun bindItemViewHolder(holder: ItemViewHolder, grade: Grade) { - val context = holder.binding.root.context - - with(holder.binding) { - gradeItemValue.run { - text = grade.entry - backgroundTintList = ColorStateList.valueOf( - context.getCompatColor(grade.getBackgroundColor(gradeColorTheme)) - ) - } - gradeItemDescription.text = when { - grade.description.isNotBlank() -> grade.description - grade.gradeSymbol.isNotBlank() -> grade.gradeSymbol - else -> context.getString(R.string.all_no_description) - } - gradeItemDate.text = grade.date.toFormattedString() - gradeItemWeight.text = "${context.getString(R.string.grade_weight)}: ${grade.weight}" - gradeItemNote.visibility = if (!grade.isRead) View.VISIBLE else View.GONE - - root.setOnClickListener { - holder.bindingAdapterPosition.let { - if (it != NO_POSITION) onClickListener(grade, it) + if (expandedPosition != NO_POSITION) { + refreshList(headers.toMutableList().apply { + addAll(headerPosition + 1, header.grades) + }) + scrollToHeaderWithSubItems(headerPosition, header.grades.size) + } else { + refreshList(headers) } } } } - private fun formatAverage(average: Double?, resources: Resources) = - if (average == null || average == .0) { - resources.getString(R.string.grade_no_average) - } else { - resources.getString(R.string.grade_average, average) + private fun formatAverage(average: Double?, resources: Resources): String { + return if (average == null || average == .0) resources.getString(R.string.grade_no_average) + else resources.getString(R.string.grade_average, average) + } + + @SuppressLint("SetTextI18n") + private fun bindItemViewHolder(holder: ItemViewHolder, grade: Grade) { + with(holder.binding) { + gradeItemValue.run { + text = grade.entry + setBackgroundResource(grade.getBackgroundColor(colorTheme)) + } + gradeItemDescription.text = when { + grade.description.isNotBlank() -> grade.description + grade.gradeSymbol.isNotBlank() -> grade.gradeSymbol + else -> root.context.getString(R.string.all_no_description) + } + gradeItemDate.text = grade.date.toFormattedString() + gradeItemWeight.text = "${root.context.getString(R.string.grade_weight)}: ${grade.weight}" + gradeItemNote.visibility = if (!grade.isRead) View.VISIBLE else View.GONE + + root.setOnClickListener { + holder.bindingAdapterPosition.let { if (it != NO_POSITION) onClickListener(grade, it) } + } } + } private class HeaderViewHolder(val binding: HeaderGradeDetailsBinding) : RecyclerView.ViewHolder(binding.root) @@ -239,10 +172,8 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter, - private val new: List - ) : DiffUtil.Callback() { + class GradeDetailsDiffUtil(private val old: List, private val new: List) : + DiffUtil.Callback() { override fun getOldListSize() = old.size diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt index 39f72f8b..28619446 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt @@ -1,53 +1,57 @@ package io.github.wulkanowy.ui.modules.grade.details -import android.app.Dialog -import android.content.res.ColorStateList import android.os.Bundle +import android.view.LayoutInflater import android.view.View import android.view.View.GONE -import androidx.core.content.ContextCompat -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.enums.GradeColorTheme import io.github.wulkanowy.databinding.DialogGradeBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.colorStringId +import io.github.wulkanowy.utils.getBackgroundColor +import io.github.wulkanowy.utils.getGradeColor +import io.github.wulkanowy.utils.lifecycleAwareVariable +import io.github.wulkanowy.utils.toFormattedString -@AndroidEntryPoint -class GradeDetailsDialog : BaseDialogFragment() { +class GradeDetailsDialog : DialogFragment() { + + private var binding: DialogGradeBinding by lifecycleAwareVariable() private lateinit var grade: Grade - private lateinit var gradeColorTheme: GradeColorTheme + private lateinit var colorScheme: String companion object { private const val ARGUMENT_KEY = "Item" - private const val COLOR_THEME_KEY = "Theme" + private const val COLOR_SCHEME_KEY = "Scheme" - fun newInstance(grade: Grade, colorTheme: GradeColorTheme) = GradeDetailsDialog().apply { - arguments = bundleOf( - ARGUMENT_KEY to grade, - COLOR_THEME_KEY to colorTheme - ) - } + fun newInstance(grade: Grade, colorScheme: String) = + GradeDetailsDialog().apply { + arguments = Bundle().apply { + putSerializable(ARGUMENT_KEY, grade) + putString(COLOR_SCHEME_KEY, colorScheme) + } + } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - grade = requireArguments().serializable(ARGUMENT_KEY) - gradeColorTheme = requireArguments().serializable(COLOR_THEME_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + grade = getSerializable(ARGUMENT_KEY) as Grade + colorScheme = getString(COLOR_SCHEME_KEY) ?: "default" + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogGradeBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogGradeBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -55,9 +59,10 @@ class GradeDetailsDialog : BaseDialogFragment() { with(binding) { gradeDialogSubject.text = grade.subject - gradeDialogWeightValue.text = grade.weight - gradeDialogWeightLayout.backgroundTintList = - ColorStateList.valueOf(requireContext().getCompatColor(grade.getGradeColor())) + gradeDialogColorAndWeightValue.run { + text = context.getString(R.string.grade_weight_value, grade.weight) + setBackgroundResource(grade.getGradeColor()) + } gradeDialogDateValue.text = grade.date.toFormattedString() gradeDialogColorValue.text = getString(grade.colorStringId) @@ -71,15 +76,12 @@ class GradeDetailsDialog : BaseDialogFragment() { gradeDialogValue.run { text = grade.entry - backgroundTintList = ColorStateList.valueOf( - ContextCompat.getColor( - requireContext(), - grade.getBackgroundColor(gradeColorTheme) - ) - ) + setBackgroundResource(grade.getBackgroundColor(colorScheme)) } - gradeDialogTeacherValue.text = grade.teacher.ifBlank { getString(R.string.all_no_data) } + gradeDialogTeacherValue.text = if (grade.teacher.isBlank()) { + getString(R.string.all_no_data) + } else grade.teacher gradeDialogDescriptionValue.text = grade.run { when { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt index 23d767a6..9d4da767 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt @@ -5,13 +5,13 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View -import android.view.View.* +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.enums.GradeColorTheme -import io.github.wulkanowy.data.enums.GradeExpandMode import io.github.wulkanowy.databinding.FragmentGradeDetailsBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment @@ -40,7 +40,6 @@ class GradeDetailsFragment : override val isViewEmpty get() = gradeDetailsAdapter.itemCount == 0 - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -80,10 +79,10 @@ class GradeDetailsFragment : else false } - override fun updateData(data: List, expandMode: GradeExpandMode, gradeColorTheme: GradeColorTheme) { + override fun updateData(data: List, isGradeExpandable: Boolean, gradeColorTheme: String) { with(gradeDetailsAdapter) { - this.gradeColorTheme = gradeColorTheme - setDataItems(data, expandMode) + colorTheme = gradeColorTheme + setDataItems(data, isGradeExpandable) notifyDataSetChanged() } } @@ -143,8 +142,8 @@ class GradeDetailsFragment : binding.gradeDetailsSwipe.isRefreshing = show } - override fun showGradeDialog(grade: Grade, colorTheme: GradeColorTheme) { - (activity as? MainActivity)?.showDialogFragment(GradeDetailsDialog.newInstance(grade, colorTheme)) + override fun showGradeDialog(grade: Grade, colorScheme: String) { + (activity as? MainActivity)?.showDialogFragment(GradeDetailsDialog.newInstance(grade, colorScheme)) } override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index 4261c507..7544d2aa 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -1,9 +1,7 @@ package io.github.wulkanowy.ui.modules.grade.details -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.enums.GradeExpandMode -import io.github.wulkanowy.data.enums.GradeSortingMode.* import io.github.wulkanowy.data.repositories.GradeRepository import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.SemesterRepository @@ -11,10 +9,15 @@ import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider +import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.ALPHABETIC +import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.DATE import io.github.wulkanowy.ui.modules.grade.GradeSubject import io.github.wulkanowy.utils.AnalyticsHelper -import kotlinx.coroutines.flow.catch +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource +import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -43,8 +46,8 @@ class GradeDetailsPresenter @Inject constructor( fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { currentSemesterId = semesterId - if (!forceRefresh) view?.showErrorView(false) loadData(semesterId, forceRefresh) + if (!forceRefresh) view?.showErrorView(false) } fun onGradeItemSelected(grade: Grade, position: Int) { @@ -66,7 +69,7 @@ class GradeDetailsPresenter @Inject constructor( } fun onMarkAsReadSelected(): Boolean { - resourceFlow { + flowWithResource { val student = studentRepository.getCurrentStudent() val semesters = semesterRepository.getSemesters(student) val semester = semesters.first { item -> item.semesterId == currentSemesterId } @@ -74,11 +77,19 @@ class GradeDetailsPresenter @Inject constructor( Timber.i("Mark as read ${unreadGrades.size} grades") gradeRepository.updateGrades(unreadGrades.map { it.apply { isRead = true } }) - } - .logResourceStatus("mark grades as read") - .onResourceSuccess { loadData(currentSemesterId, false) } - .onResourceError(errorHandler::dispatch) - .launch("mark") + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Select mark grades as read") + Status.SUCCESS -> { + Timber.i("Mark as read result: Success") + loadData(currentSemesterId, false) + } + Status.ERROR -> { + Timber.i("Mark as read result: An exception occurred") + errorHandler.dispatch(it.error!!) + } + } + }.launch("mark") return true } @@ -102,7 +113,7 @@ class GradeDetailsPresenter @Inject constructor( fun onParentViewReselected() { view?.run { if (!isViewEmpty) { - if (preferencesRepository.gradeExpandMode != GradeExpandMode.ALWAYS_EXPANDED) collapseAllItems() + if (preferencesRepository.isGradeExpandable) collapseAllItems() scrollToStart() } } @@ -125,50 +136,68 @@ class GradeDetailsPresenter @Inject constructor( } private fun loadData(semesterId: Int, forceRefresh: Boolean) { - flatResourceFlow { + Timber.i("Loading grade details data started") + + flowWithResourceIn { val student = studentRepository.getCurrentStudent() averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh) - } - .logResourceStatus("load grade details") - .onResourceData { - val gradeItems = createGradeItems(it) - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showContent(gradeItems.isNotEmpty()) - showEmpty(gradeItems.isEmpty()) - updateNewGradesAmount(it) + }.onEach { + Timber.d("Loading grade details status: ${it.status}, data: ${it.data != null}") + when (it.status) { + Status.LOADING -> { + val items = createGradeItems(it.data.orEmpty()) + if (items.isNotEmpty()) { + Timber.i("Loading grade details result: load cached data") + view?.run { + updateNewGradesAmount(it.data.orEmpty()) + enableSwipe(true) + showRefresh(true) + showProgress(false) + showEmpty(false) + showContent(true) + updateData( + data = items, + isGradeExpandable = preferencesRepository.isGradeExpandable, + gradeColorTheme = preferencesRepository.gradeColorTheme + ) + notifyParentDataLoaded(semesterId) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading grade details result: Success") + updateNewGradesAmount(it.data!!) updateMarkAsDoneButton() - updateData( - data = gradeItems, - expandMode = preferencesRepository.gradeExpandMode, - preferencesRepository.gradeColorTheme + val items = createGradeItems(it.data) + view?.run { + showEmpty(items.isEmpty()) + showErrorView(false) + showContent(items.isNotEmpty()) + updateData( + data = items, + isGradeExpandable = preferencesRepository.isGradeExpandable, + gradeColorTheme = preferencesRepository.gradeColorTheme + ) + } + analytics.logEvent( + "load_data", + "type" to "grade_details", + "items" to it.data.size ) } - } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "grade_details", - "items" to it.size - ) - } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showRefresh(false) - showProgress(false) - notifyParentDataLoaded(semesterId) + Status.ERROR -> { + Timber.i("Loading grade details result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .catch { - errorHandler.dispatch(it) - view?.notifyParentDataLoaded(semesterId) + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded(semesterId) } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun updateNewGradesAmount(grades: List) { @@ -184,7 +213,6 @@ class GradeDetailsPresenter @Inject constructor( setErrorDetails(message) showErrorView(true) showEmpty(false) - showProgress(false) } else showError(message, error) } } @@ -196,15 +224,10 @@ class GradeDetailsPresenter @Inject constructor( gradesWithAverages.filter { it.grades.isNotEmpty() } } else gradesWithAverages } - .let { gradeSubjects -> + .let { when (preferencesRepository.gradeSortingMode) { - DATE -> gradeSubjects.sortedByDescending { gradeDetailsWithAverage -> - gradeDetailsWithAverage.grades.maxByOrNull { it.date }?.date - } - ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage -> - gradeDetailsWithAverage.subject.lowercase() - } - AVERAGE -> gradeSubjects.sortedByDescending { it.average } + DATE -> it.sortedByDescending { gradeDetailsWithAverage -> gradeDetailsWithAverage.grades.firstOrNull()?.date } + ALPHABETIC -> it.sortedBy { gradeDetailsWithAverage -> gradeDetailsWithAverage.subject.lowercase() } } } .map { (subject, average, points, _, grades) -> @@ -212,31 +235,27 @@ class GradeDetailsPresenter @Inject constructor( .sortedByDescending { it.date } .map { GradeDetailsItem(it, ViewType.ITEM) } - val gradeDetailsItems = listOf( - GradeDetailsItem( - GradeDetailsHeader( - subject = subject, - average = average, - pointsSum = points, - grades = subItems - ).apply { - newGrades = grades.filter { grade -> !grade.isRead }.size - }, ViewType.HEADER - ) - ) - - if (preferencesRepository.gradeExpandMode == GradeExpandMode.ALWAYS_EXPANDED) { - gradeDetailsItems + subItems - } else { - gradeDetailsItems - } + listOf(GradeDetailsItem(GradeDetailsHeader( + subject = subject, + average = average, + pointsSum = points, + grades = subItems + ).apply { + newGrades = grades.filter { grade -> !grade.isRead }.size + }, ViewType.HEADER)) + if (preferencesRepository.isGradeExpandable) emptyList() else subItems }.flatten() } private fun updateGrade(grade: Grade) { - resourceFlow { gradeRepository.updateGrade(grade) } - .logResourceStatus("update grade result ${grade.id}") - .onResourceError(errorHandler::dispatch) - .launch("update") + flowWithResource { gradeRepository.updateGrade(grade) }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Attempt to update grade ${grade.id}") + Status.SUCCESS -> Timber.i("Update grade result: Success") + Status.ERROR -> { + Timber.i("Update grade result: An exception occurred") + errorHandler.dispatch(it.error!!) + } + } + }.launch("update") } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt index 491bf300..e71fcc3c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt @@ -1,8 +1,6 @@ package io.github.wulkanowy.ui.modules.grade.details import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.enums.GradeColorTheme -import io.github.wulkanowy.data.enums.GradeExpandMode import io.github.wulkanowy.ui.base.BaseView interface GradeDetailsView : BaseView { @@ -11,7 +9,7 @@ interface GradeDetailsView : BaseView { fun initView() - fun updateData(data: List, expandMode: GradeExpandMode, gradeColorTheme: GradeColorTheme) + fun updateData(data: List, isGradeExpandable: Boolean, gradeColorTheme: String) fun updateItem(item: Grade, position: Int) @@ -23,7 +21,7 @@ interface GradeDetailsView : BaseView { fun collapseAllItems() - fun showGradeDialog(grade: Grade, colorTheme: GradeColorTheme) + fun showGradeDialog(grade: Grade, colorScheme: String) fun showContent(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt index 3fce8d57..bf0b2014 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt @@ -9,13 +9,17 @@ import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.github.mikephil.charting.components.Legend import com.github.mikephil.charting.components.LegendEntry -import com.github.mikephil.charting.data.* +import com.github.mikephil.charting.data.BarData +import com.github.mikephil.charting.data.BarDataSet +import com.github.mikephil.charting.data.BarEntry +import com.github.mikephil.charting.data.PieData +import com.github.mikephil.charting.data.PieDataSet +import com.github.mikephil.charting.data.PieEntry import com.github.mikephil.charting.formatter.ValueFormatter import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.GradePartialStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics -import io.github.wulkanowy.data.enums.GradeColorTheme import io.github.wulkanowy.data.pojos.GradeStatisticsItem import io.github.wulkanowy.databinding.ItemGradeStatisticsBarBinding import io.github.wulkanowy.databinding.ItemGradeStatisticsHeaderBinding @@ -30,7 +34,7 @@ class GradeStatisticsAdapter @Inject constructor() : var items = emptyList() - lateinit var gradeColorTheme: GradeColorTheme + var theme: String = "vulcan" var showAllSubjectsOnList: Boolean = false @@ -116,9 +120,7 @@ class GradeStatisticsAdapter @Inject constructor() : } ) - binding.gradeStatisticsTypeSwitch.addOnButtonCheckedListener { _, checkedId, isChecked -> - if (!isChecked) return@addOnButtonCheckedListener - + binding.gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId -> currentDataType = when (checkedId) { R.id.gradeStatisticsTypePartial -> GradeStatisticsItem.DataType.PARTIAL R.id.gradeStatisticsTypeSemester -> GradeStatisticsItem.DataType.SEMESTER @@ -133,50 +135,20 @@ class GradeStatisticsAdapter @Inject constructor() : binding: ItemGradeStatisticsPieBinding, partials: GradePartialStatistics ) { - val studentAverage = partials.studentAverage.takeIf { it.isNotEmpty() }?.let { - binding.root.context.getString(R.string.grade_statistics_student_average, it) - } - bindPieChart( - binding = binding, - subject = partials.subject, - average = partials.classAverage, - studentValue = studentAverage, - amounts = partials.classAmounts - ) + bindPieChart(binding, partials.subject, partials.classAverage, partials.classAmounts) } private fun bindSemesterChart( binding: ItemGradeStatisticsPieBinding, semester: GradeSemesterStatistics ) { - val studentAverage = semester.studentAverage.takeIf { it.isNotBlank() } - val studentGrade = semester.studentGrade.takeIf { it != 0 } - - val studentValue = when { - studentAverage != null -> binding.root.context.getString( - R.string.grade_statistics_student_average, - studentAverage - ) - studentGrade != null -> binding.root.context.getString( - R.string.grade_statistics_student_grade, - studentGrade.toString() - ) - else -> null - } - bindPieChart( - binding = binding, - subject = semester.subject, - average = semester.classAverage, - studentValue = studentValue, - amounts = semester.amounts - ) + bindPieChart(binding, semester.subject, semester.average, semester.amounts) } private fun bindPieChart( binding: ItemGradeStatisticsPieBinding, subject: String, average: String, - studentValue: String?, amounts: List ) { with(binding.gradeStatisticsPieTitle) { @@ -184,8 +156,8 @@ class GradeStatisticsAdapter @Inject constructor() : visibility = if (items.size == 1 || !showAllSubjectsOnList) GONE else VISIBLE } - val gradeColors = when (gradeColorTheme) { - GradeColorTheme.VULCAN -> vulcanGradeColors + val gradeColors = when (theme) { + "vulcan" -> vulcanGradeColors else -> materialGradeColors } @@ -235,13 +207,13 @@ class GradeStatisticsAdapter @Inject constructor() : val numberOfGradesString = amounts.fold(0) { acc, it -> acc + it } .let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } val averageString = - binding.root.context.getString(R.string.grade_statistics_class_average, average) + binding.root.context.getString(R.string.grade_statistics_average, average) minAngleForSlices = 25f description.isEnabled = false centerText = numberOfGradesString + ("\n\n" + averageString).takeIf { average.isNotBlank() } - .orEmpty() + studentValue?.let { "\n$it" }.orEmpty() + .orEmpty() setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground)) setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary)) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt index edc384c5..0adac300 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt @@ -7,7 +7,6 @@ import android.widget.TextView import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.enums.GradeColorTheme import io.github.wulkanowy.data.pojos.GradeStatisticsItem import io.github.wulkanowy.databinding.FragmentGradeStatisticsBinding import io.github.wulkanowy.ui.base.BaseFragment @@ -15,7 +14,6 @@ import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeView import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.serializable import io.github.wulkanowy.utils.setOnItemSelectedListener import javax.inject.Inject @@ -34,7 +32,6 @@ class GradeStatisticsFragment : companion object { private const val SAVED_CHART_TYPE = "CURRENT_TYPE" - private const val SAVED_SUBJECT_NAME = "SUBJECT_NAME" fun newInstance() = GradeStatisticsFragment() } @@ -46,11 +43,10 @@ class GradeStatisticsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentGradeStatisticsBinding.bind(view) - messageContainer = binding.gradeStatisticsRecycler + messageContainer = binding.gradeStatisticsSwipe presenter.onAttachView( - view = this, - type = savedInstanceState?.serializable(SAVED_CHART_TYPE), - subjectName = savedInstanceState?.serializable(SAVED_SUBJECT_NAME), + this, + savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType ) } @@ -59,7 +55,6 @@ class GradeStatisticsFragment : with(binding.gradeStatisticsRecycler) { layoutManager = LinearLayoutManager(requireContext()) - statisticsAdapter.currentDataType = presenter.currentType adapter = statisticsAdapter } @@ -73,7 +68,7 @@ class GradeStatisticsFragment : } with(binding) { - gradeStatisticsSubjectsContainer.elevation = requireContext().dpToPx(1f) + gradeStatisticsSubjectsContainer.setElevationCompat(requireContext().dpToPx(1f)) gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) gradeStatisticsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) @@ -85,8 +80,7 @@ class GradeStatisticsFragment : } } - override fun updateSubjects(data: List, selectedIndex: Int) { - binding.gradeStatisticsSubjects.setSelection(selectedIndex) + override fun updateSubjects(data: ArrayList) { with(subjectsAdapter) { clear() addAll(data) @@ -96,12 +90,12 @@ class GradeStatisticsFragment : override fun updateData( newItems: List, - newTheme: GradeColorTheme, + newTheme: String, showAllSubjectsOnStatisticsList: Boolean ) { with(statisticsAdapter) { showAllSubjectsOnList = showAllSubjectsOnStatisticsList - gradeColorTheme = newTheme + theme = newTheme items = newItems notifyDataSetChanged() } @@ -166,7 +160,6 @@ class GradeStatisticsFragment : override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putSerializable(SAVED_CHART_TYPE, presenter.currentType) - outState.putSerializable(SAVED_SUBJECT_NAME, presenter.currentSubjectName) } override fun onDestroyView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt index aa0e5999..53eccad6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt @@ -1,12 +1,19 @@ package io.github.wulkanowy.ui.modules.grade.statistics -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Subject import io.github.wulkanowy.data.pojos.GradeStatisticsItem -import io.github.wulkanowy.data.repositories.* +import io.github.wulkanowy.data.repositories.GradeStatisticsRepository +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.SubjectRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -24,22 +31,16 @@ class GradeStatisticsPresenter @Inject constructor( private var currentSemesterId = 0 - var currentSubjectName: String = "Wszystkie" - private set + private var currentSubjectName: String = "Wszystkie" private lateinit var lastError: Throwable var currentType: GradeStatisticsItem.DataType = GradeStatisticsItem.DataType.PARTIAL private set - fun onAttachView( - view: GradeStatisticsView, - type: GradeStatisticsItem.DataType?, - subjectName: String? - ) { + fun onAttachView(view: GradeStatisticsView, type: GradeStatisticsItem.DataType?) { super.onAttachView(view) currentType = type ?: GradeStatisticsItem.DataType.PARTIAL - currentSubjectName = subjectName ?: currentSubjectName view.initView() errorHandler.showErrorMessage = ::showErrorViewOnError } @@ -118,26 +119,28 @@ class GradeStatisticsPresenter @Inject constructor( } private fun loadSubjects() { - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) subjectRepository.getSubjects(student, semester) - } - .logResourceStatus("load grade stats subjects") - .onResourceData { - subjects = it - view?.run { - showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) - updateSubjects( - data = it.map { subject -> subject.name }, - selectedIndex = it.indexOfFirst { subject -> - subject.name == currentSubjectName - }, - ) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading grade stats subjects started") + Status.SUCCESS -> { + subjects = it.data!! + + Timber.i("Loading grade stats subjects result: Success") + view?.run { + view?.updateSubjects(ArrayList(it.data.map { subject -> subject.name })) + showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) + } + } + Status.ERROR -> { + Timber.i("Loading grade stats subjects result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceError(errorHandler::dispatch) - .launch("subjects") + }.launch("subjects") } private fun loadDataByType( @@ -148,13 +151,11 @@ class GradeStatisticsPresenter @Inject constructor( ) { Timber.i("Loading grade stats data started") + currentSubjectName = + if (preferencesRepository.showAllSubjectsOnStatisticsList) "Wszystkie" else subjectName currentType = type - currentSubjectName = when { - preferencesRepository.showAllSubjectsOnStatisticsList -> "Wszystkie" - else -> subjectName - } - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semesters = semesterRepository.getSemesters(student) val semester = semesters.first { item -> item.semesterId == semesterId } @@ -187,43 +188,58 @@ class GradeStatisticsPresenter @Inject constructor( } } } - } - .logResourceStatus("load grade stats data") - .mapResourceData { - val isNoContent = checkIsNoContent(it, type) - if (isNoContent) emptyList() else it - } - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showEmpty(it.isEmpty()) - updateData( - newItems = it, - newTheme = preferencesRepository.gradeColorTheme, - showAllSubjectsOnStatisticsList = preferencesRepository.showAllSubjectsOnStatisticsList + }.onEach { + when (it.status) { + Status.LOADING -> { + val isNoContent = it.data == null || checkIsNoContent(it.data, type) + if (!isNoContent) { + view?.run { + showEmpty(isNoContent) + showErrorView(false) + enableSwipe(true) + showRefresh(true) + showProgress(false) + updateData( + if (isNoContent) emptyList() else it.data!!, + preferencesRepository.gradeColorTheme, + preferencesRepository.showAllSubjectsOnStatisticsList + ) + showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading grade stats result: Success") + view?.run { + val isNoContent = checkIsNoContent(it.data!!, type) + showEmpty(isNoContent) + showErrorView(false) + updateData( + if (isNoContent) emptyList() else it.data, + preferencesRepository.gradeColorTheme, + preferencesRepository.showAllSubjectsOnStatisticsList + ) + showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) + } + analytics.logEvent( + "load_data", + "type" to "grade_statistics", + "items" to it.data!!.size ) } - } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "grade_statistics", - "items" to it.size - ) - } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showRefresh(false) - showProgress(false) - notifyParentDataLoaded(semesterId) + Status.ERROR -> { + Timber.i("Loading grade stats result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceError(errorHandler::dispatch) - .launch("load") + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded(semesterId) + } + }.launch("load") } private fun checkIsNoContent( @@ -238,8 +254,7 @@ class GradeStatisticsPresenter @Inject constructor( items.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0 } GradeStatisticsItem.DataType.POINTS -> { - items.firstOrNull()?.points?.let { points -> points.student == .0 && points.others == .0 } - ?: false + items.firstOrNull()?.points?.let { points -> points.student == .0 && points.others == .0 } ?: false } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt index 4333bb0a..40511817 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.grade.statistics -import io.github.wulkanowy.data.enums.GradeColorTheme import io.github.wulkanowy.data.pojos.GradeStatisticsItem import io.github.wulkanowy.ui.base.BaseView @@ -12,11 +11,11 @@ interface GradeStatisticsView : BaseView { fun initView() - fun updateSubjects(data: List, selectedIndex: Int) + fun updateSubjects(data: ArrayList) fun updateData( newItems: List, - newTheme: GradeColorTheme, + newTheme: String, showAllSubjectsOnStatisticsList: Boolean ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt index 8dcade56..0754361c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt @@ -10,8 +10,7 @@ import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.ItemGradeSummaryBinding import io.github.wulkanowy.databinding.ScrollableHeaderGradeSummaryBinding -import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid -import io.github.wulkanowy.utils.calcFinalAverage +import io.github.wulkanowy.utils.calcAverage import java.util.Locale import javax.inject.Inject @@ -26,10 +25,6 @@ class GradeSummaryAdapter @Inject constructor( var items = emptyList() - var onCalculatedHelpClickListener: () -> Unit = {} - - var onFinalHelpClickListener: () -> Unit = {} - override fun getItemCount() = items.size + if (items.isNotEmpty()) 1 else 0 override fun getItemViewType(position: Int) = when (position) { @@ -62,10 +57,10 @@ class GradeSummaryAdapter @Inject constructor( if (items.isEmpty()) return val context = binding.root.context - val finalItemsCount = items.count { isGradeValid(it.finalGrade) } + val finalItemsCount = items.count { it.finalGrade.matches("[0-6][+-]?".toRegex()) } val calculatedItemsCount = items.count { value -> value.average != 0.0 } val allItemsCount = items.count { !it.subject.equals("zachowanie", true) } - val finalAverage = items.calcFinalAverage( + val finalAverage = items.calcAverage( preferencesRepository.gradePlusModifier, preferencesRepository.gradeMinusModifier ) @@ -88,9 +83,6 @@ class GradeSummaryAdapter @Inject constructor( calculatedItemsCount, allItemsCount ) - - gradeSummaryCalculatedAverageHelp.setOnClickListener { onCalculatedHelpClickListener() } - gradeSummaryFinalAverageHelp.setOnClickListener { onFinalHelpClickListener() } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt index abd0b13c..0ac16fb3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryFragment.kt @@ -5,9 +5,7 @@ import android.view.View import android.view.View.GONE import android.view.View.INVISIBLE import android.view.View.VISIBLE -import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.GradeSummary @@ -50,11 +48,6 @@ class GradeSummaryFragment : } override fun initView() { - with(gradeSummaryAdapter) { - onCalculatedHelpClickListener = presenter::onCalculatedAverageHelpClick - onFinalHelpClickListener = presenter::onFinalAverageHelpClick - } - with(binding.gradeSummaryRecycler) { layoutManager = LinearLayoutManager(context) adapter = gradeSummaryAdapter @@ -62,11 +55,7 @@ class GradeSummaryFragment : with(binding) { gradeSummarySwipe.setOnRefreshListener(presenter::onSwipeRefresh) gradeSummarySwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) - gradeSummarySwipe.setProgressBackgroundColorSchemeColor( - requireContext().getThemeAttrColor( - R.attr.colorSwipeRefresh - ) - ) + gradeSummarySwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) gradeSummaryErrorRetry.setOnClickListener { presenter.onRetry() } gradeSummaryErrorDetails.setOnClickListener { presenter.onDetailsClick() } } @@ -118,22 +107,6 @@ class GradeSummaryFragment : binding.gradeSummarySwipe.isRefreshing = show } - override fun showCalculatedAverageHelpDialog() { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.grade_summary_calculated_average_help_dialog_title) - .setMessage(R.string.grade_summary_calculated_average_help_dialog_message) - .setPositiveButton(R.string.all_close) { _, _ -> } - .show() - } - - override fun showFinalAverageHelpDialog() { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.grade_summary_final_average_help_dialog_title) - .setMessage(R.string.grade_summary_final_average_help_dialog_message) - .setPositiveButton(R.string.all_close) { _, _ -> } - .show() - } - override fun onParentLoadData(semesterId: Int, forceRefresh: Boolean) { presenter.onParentViewLoadData(semesterId, forceRefresh) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt index 4d5a43d8..7adfd7e5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -1,23 +1,22 @@ package io.github.wulkanowy.ui.modules.grade.summary -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.enums.GradeSortingMode -import io.github.wulkanowy.data.enums.GradeSortingMode.* -import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.ui.modules.grade.GradeSubject import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject class GradeSummaryPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, - private val preferencesRepository: PreferencesRepository, private val averageProvider: GradeAverageProvider, private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { @@ -38,40 +37,56 @@ class GradeSummaryPresenter @Inject constructor( } private fun loadData(semesterId: Int, forceRefresh: Boolean) { - flatResourceFlow { + Timber.i("Loading grade summary started") + + flowWithResourceIn { val student = studentRepository.getCurrentStudent() averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh) - } - .logResourceStatus("load grade summary", showData = true) - .mapResourceData { createGradeSummaryItems(it) } - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) - updateData(it) + }.onEach { + Timber.d("Loading grade summary status: ${it.status}, data: ${it.data != null}") + when (it.status) { + Status.LOADING -> { + val items = createGradeSummaryItems(it.data.orEmpty()) + if (items.isNotEmpty()) { + Timber.i("Loading grade summary result: load cached data") + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showEmpty(false) + showContent(true) + updateData(items) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading grade summary result: Success") + val items = createGradeSummaryItems(it.data!!) + view?.run { + showEmpty(items.isEmpty()) + showContent(items.isNotEmpty()) + showErrorView(false) + updateData(items) + } + analytics.logEvent( + "load_data", + "type" to "grade_summary", + "items" to it.data.size + ) + } + Status.ERROR -> { + Timber.i("Loading grade summary result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "grade_summary", - "items" to it.size - ) + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded(semesterId) } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showRefresh(false) - showProgress(false) - notifyParentDataLoaded(semesterId) - } - } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -120,37 +135,19 @@ class GradeSummaryPresenter @Inject constructor( cancelJobs("load") } - fun onCalculatedAverageHelpClick() { - view?.showCalculatedAverageHelpDialog() - } - - fun onFinalAverageHelpClick() { - view?.showFinalAverageHelpDialog() - } - private fun createGradeSummaryItems(items: List): List { return items .filter { !checkEmpty(it) } - .let { gradeSubjects -> - when (preferencesRepository.gradeSortingMode) { - DATE -> gradeSubjects.sortedByDescending { gradeDetailsWithAverage -> - gradeDetailsWithAverage.grades.maxByOrNull { it.date }?.date - } - ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage -> - gradeDetailsWithAverage.subject.lowercase() - } - AVERAGE -> gradeSubjects.sortedByDescending { it.average } - } - } + .sortedBy { it.subject } .map { it.summary.copy(average = it.average) } } private fun checkEmpty(gradeSummary: GradeSubject): Boolean { return gradeSummary.run { summary.finalGrade.isBlank() - && summary.predictedGrade.isBlank() - && average == .0 - && points.isBlank() + && summary.predictedGrade.isBlank() + && average == .0 + && points.isBlank() } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt index 156731c3..974d9141 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryView.kt @@ -33,10 +33,6 @@ interface GradeSummaryView : BaseView { fun showEmpty(show: Boolean) - fun showCalculatedAverageHelpDialog() - - fun showFinalAverageHelpDialog() - fun notifyParentDataLoaded(semesterId: Int) fun notifyParentRefresh() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt index 0381acf3..1d9434dc 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt @@ -10,7 +10,6 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.databinding.FragmentHomeworkBinding import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.homework.add.HomeworkAddDialog import io.github.wulkanowy.ui.modules.homework.details.HomeworkDetailsDialog import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView @@ -21,7 +20,7 @@ import javax.inject.Inject @AndroidEntryPoint class HomeworkFragment : BaseFragment(R.layout.fragment_homework), - HomeworkView, MainView.TitledView, MainView.MainChildView { + HomeworkView, MainView.TitledView { @Inject lateinit var presenter: HomeworkPresenter @@ -65,9 +64,7 @@ class HomeworkFragment : BaseFragment(R.layout.fragment homeworkPreviousButton.setOnClickListener { presenter.onPreviousDay() } homeworkNextButton.setOnClickListener { presenter.onNextDay() } - openAddHomeworkButton.setOnClickListener { presenter.onHomeworkAddButtonClicked() } - - homeworkNavContainer.elevation = requireContext().dpToPx(3f) + homeworkNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -125,22 +122,10 @@ class HomeworkFragment : BaseFragment(R.layout.fragment binding.homeworkNextButton.visibility = if (show) VISIBLE else View.INVISIBLE } - override fun showHomeworkDialog(homework: Homework) { + override fun showTimetableDialog(homework: Homework) { (activity as? MainActivity)?.showDialogFragment(HomeworkDetailsDialog.newInstance(homework)) } - override fun showAddHomeworkDialog() { - (activity as? MainActivity)?.showDialogFragment(HomeworkAddDialog()) - } - - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onViewReselected() - } - - override fun resetView() { - binding.homeworkRecycler.smoothScrollToPosition(0) - } - override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt index 6b263e26..11c54dc2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt @@ -1,13 +1,21 @@ package io.github.wulkanowy.ui.modules.homework -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.repositories.HomeworkRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEach @@ -70,70 +78,68 @@ class HomeworkPresenter @Inject constructor( fun onHomeworkItemSelected(homework: Homework) { Timber.i("Select homework item ${homework.id}") - view?.showHomeworkDialog(homework) - } - - fun onHomeworkAddButtonClicked() { - view?.showAddHomeworkDialog() + view?.showTimetableDialog(homework) } 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") + }.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 homework data started") - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) - homeworkRepository.getHomework( - student = student, - semester = semester, - start = currentDate, - end = currentDate, - forceRefresh = forceRefresh - ) - } - .logResourceStatus("loading homework") - .mapResourceData { createHomeworkItem(it) } - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) - updateData(it) + homeworkRepository.getHomework(student, semester, currentDate, currentDate, forceRefresh) + }.onEach { + when (it.status) { + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(createHomeworkItem(it.data)) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading homework result: Success") + view?.apply { + updateData(createHomeworkItem(it.data!!)) + showEmpty(it.data.isEmpty()) + showErrorView(false) + showContent(it.data.isNotEmpty()) + } + analytics.logEvent( + "load_data", + "type" to "homework", + "items" to it.data!!.size + ) + } + Status.ERROR -> { + Timber.i("Loading homework result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "homework", - "items" to it.size - ) + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showProgress(false) - showRefresh(false) - } - } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -149,10 +155,9 @@ class HomeworkPresenter @Inject constructor( private fun createHomeworkItem(items: List): List> { return items.groupBy { it.date }.toSortedMap().map { (date, exams) -> - listOf(HomeworkItem(date, HomeworkItem.ViewType.HEADER)) + exams.reversed() - .map { exam -> - HomeworkItem(exam, HomeworkItem.ViewType.ITEM) - } + listOf(HomeworkItem(date, HomeworkItem.ViewType.HEADER)) + exams.reversed().map { exam -> + HomeworkItem(exam, HomeworkItem.ViewType.ITEM) + } }.flatten() } @@ -175,23 +180,8 @@ class HomeworkPresenter @Inject constructor( view?.apply { showPreButton(!currentDate.minusDays(7).isHolidays) showNextButton(!currentDate.plusDays(7).isHolidays) - updateNavigationWeek( - "${currentDate.monday.toFormattedString("dd.MM")} - " + - currentDate.sunday.toFormattedString("dd.MM") - ) - } - } - - fun onViewReselected() { - Timber.i("Homework view is reselected") - - baseDate = LocalDate.now().nextOrSameSchoolDay - - if (currentDate != baseDate) { - reloadView(baseDate) - loadData() - } else if (view?.isViewEmpty == false) { - view?.resetView() + updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " + + currentDate.sunday.toFormattedString("dd.MM")) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt index 56ba6c89..a1d6a04a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt @@ -33,9 +33,5 @@ interface HomeworkView : BaseView { fun showNextButton(show: Boolean) - fun showHomeworkDialog(homework: Homework) - - fun showAddHomeworkDialog() - - fun resetView() + fun showTimetableDialog(homework: Homework) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt deleted file mode 100644 index c51370ea..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt +++ /dev/null @@ -1,110 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework.add - -import android.app.Dialog -import android.os.Bundle -import android.view.View -import androidx.core.widget.doOnTextChanged -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.databinding.DialogHomeworkAddBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear -import io.github.wulkanowy.utils.openMaterialDatePicker -import io.github.wulkanowy.utils.toFormattedString -import java.time.LocalDate -import javax.inject.Inject - -@AndroidEntryPoint -class HomeworkAddDialog : BaseDialogFragment(), HomeworkAddView { - - @Inject - lateinit var presenter: HomeworkAddPresenter - - //todo: move it to presenter - private var date: LocalDate? = null - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogHomeworkAddBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - with(binding) { - homeworkDialogSubjectEdit.doOnTextChanged { _, _, _, _ -> - homeworkDialogSubject.error = null - homeworkDialogSubject.isErrorEnabled = false - } - homeworkDialogDateEdit.doOnTextChanged { _, _, _, _ -> - homeworkDialogDate.error = null - homeworkDialogDate.isErrorEnabled = false - } - homeworkDialogContentEdit.doOnTextChanged { _, _, _, _ -> - homeworkDialogContent.error = null - homeworkDialogContent.isErrorEnabled = false - } - homeworkDialogClose.setOnClickListener { dismiss() } - homeworkDialogDateEdit.setOnClickListener { presenter.showDatePicker(date) } - homeworkDialogAdd.setOnClickListener { - presenter.onAddHomeworkClicked( - subject = homeworkDialogSubjectEdit.text?.toString(), - teacher = homeworkDialogTeacherEdit.text?.toString(), - date = homeworkDialogDateEdit.text?.toString(), - content = homeworkDialogContentEdit.text?.toString() - ) - } - } - } - - override fun showSuccessMessage() { - showMessage(getString(R.string.homework_add_success)) - } - - override fun setErrorSubjectRequired() { - with(binding.homeworkDialogSubject) { - isErrorEnabled = true - error = getString(R.string.error_field_required) - } - } - - override fun setErrorDateRequired() { - with(binding.homeworkDialogDate) { - isErrorEnabled = true - error = getString(R.string.error_field_required) - } - } - - override fun setErrorContentRequired() { - with(binding.homeworkDialogContent) { - isErrorEnabled = true - error = getString(R.string.error_field_required) - } - } - - override fun closeDialog() { - dismiss() - } - - override fun showDatePickerDialog(selectedDate: LocalDate) { - openMaterialDatePicker( - selected = selectedDate, - rangeStart = LocalDate.now(), - rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear, - onDateSelected = { - date = it - binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString()) - } - ) - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddPresenter.kt deleted file mode 100644 index a21f6aef..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddPresenter.kt +++ /dev/null @@ -1,87 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework.add - -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.onResourceError -import io.github.wulkanowy.data.onResourceSuccess -import io.github.wulkanowy.data.repositories.HomeworkRepository -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.toLocalDate -import timber.log.Timber -import java.time.LocalDate -import javax.inject.Inject - -class HomeworkAddPresenter @Inject constructor( - errorHandler: ErrorHandler, - studentRepository: StudentRepository, - private val homeworkRepository: HomeworkRepository, - private val semesterRepository: SemesterRepository -) : BasePresenter(errorHandler, studentRepository) { - - override fun onAttachView(view: HomeworkAddView) { - super.onAttachView(view) - view.initView() - Timber.i("Homework details view was initialized") - } - - fun showDatePicker(date: LocalDate?) { - view?.showDatePickerDialog(date ?: LocalDate.now()) - } - - fun onAddHomeworkClicked(subject: String?, teacher: String?, date: String?, content: String?) { - var isError = false - - if (subject.isNullOrBlank()) { - view?.setErrorSubjectRequired() - isError = true - } - - if (date.isNullOrBlank()) { - view?.setErrorDateRequired() - isError = true - } - - if (content.isNullOrBlank()) { - view?.setErrorContentRequired() - isError = true - } - - if (!isError) { - saveHomework(subject!!, teacher.orEmpty(), date!!.toLocalDate(), content!!) - } - } - - private fun saveHomework(subject: String, teacher: String, date: LocalDate, content: String) { - resourceFlow { - val student = studentRepository.getCurrentStudent() - val semester = semesterRepository.getCurrentSemester(student) - val entryDate = LocalDate.now() - homeworkRepository.saveHomework( - Homework( - semesterId = semester.semesterId, - studentId = student.studentId, - date = date, - entryDate = entryDate, - subject = subject, - content = content, - teacher = teacher, - teacherSymbol = "", - attachments = emptyList(), - ).apply { isAddedByUser = true } - ) - } - .logResourceStatus("homework insert") - .onResourceSuccess { - view?.run { - showSuccessMessage() - closeDialog() - } - } - .onResourceError(errorHandler::dispatch) - .launch("add_homework") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddView.kt deleted file mode 100644 index 91414ae2..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddView.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework.add - -import io.github.wulkanowy.ui.base.BaseView -import java.time.LocalDate - -interface HomeworkAddView : BaseView { - - fun initView() - - fun showSuccessMessage() - - fun setErrorSubjectRequired() - - fun setErrorDateRequired() - - fun setErrorContentRequired() - - fun closeDialog() - - fun showDatePickerDialog(selectedDate: LocalDate) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt index 1ad2a0e3..cd9a7e85 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt @@ -5,12 +5,10 @@ import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.databinding.ItemHomeworkDialogAttachmentBinding import io.github.wulkanowy.databinding.ItemHomeworkDialogAttachmentsHeaderBinding import io.github.wulkanowy.databinding.ItemHomeworkDialogDetailsBinding -import io.github.wulkanowy.utils.ifNullOrBlank import io.github.wulkanowy.utils.toFormattedString import javax.inject.Inject @@ -31,9 +29,13 @@ class HomeworkDetailsAdapter @Inject constructor() : attachments = value?.attachments.orEmpty() } + var isHomeworkFullscreen = false + var onAttachmentClickListener: (url: String) -> Unit = {} - var onDeleteClickListener: (homework: Homework) -> Unit = {} + var onFullScreenClickListener = {} + + var onFullScreenExitClickListener = {} override fun getItemCount() = 1 + if (attachments.isNotEmpty()) attachments.size + 1 else 0 @@ -47,15 +49,9 @@ class HomeworkDetailsAdapter @Inject constructor() : val inflater = LayoutInflater.from(parent.context) return when (viewType) { - ViewType.ATTACHMENTS_HEADER.id -> AttachmentsHeaderViewHolder( - ItemHomeworkDialogAttachmentsHeaderBinding.inflate(inflater, parent, false) - ) - ViewType.ATTACHMENT.id -> AttachmentViewHolder( - ItemHomeworkDialogAttachmentBinding.inflate(inflater, parent, false) - ) - else -> DetailsViewHolder( - ItemHomeworkDialogDetailsBinding.inflate(inflater, parent, false) - ) + ViewType.ATTACHMENTS_HEADER.id -> AttachmentsHeaderViewHolder(ItemHomeworkDialogAttachmentsHeaderBinding.inflate(inflater, parent, false)) + ViewType.ATTACHMENT.id -> AttachmentViewHolder(ItemHomeworkDialogAttachmentBinding.inflate(inflater, parent, false)) + else -> DetailsViewHolder(ItemHomeworkDialogDetailsBinding.inflate(inflater, parent, false)) } } @@ -67,17 +63,23 @@ class HomeworkDetailsAdapter @Inject constructor() : } private fun bindDetailsViewHolder(holder: DetailsViewHolder) { - val noDataString = holder.binding.root.context.getString(R.string.all_no_data) - with(holder.binding) { homeworkDialogDate.text = homework?.date?.toFormattedString() homeworkDialogEntryDate.text = homework?.entryDate?.toFormattedString() - homeworkDialogSubject.text = homework?.subject.ifNullOrBlank { noDataString } - homeworkDialogTeacher.text = homework?.teacher.ifNullOrBlank { noDataString } - homeworkDialogContent.text = homework?.content.ifNullOrBlank { noDataString } - homeworkDialogDelete.visibility = if (homework?.isAddedByUser == true) VISIBLE else GONE - homeworkDialogDelete.setOnClickListener { - onDeleteClickListener(homework!!) + homeworkDialogSubject.text = homework?.subject + homeworkDialogTeacher.text = homework?.teacher + homeworkDialogContent.text = homework?.content + homeworkDialogFullScreen.visibility = if (isHomeworkFullscreen) GONE else VISIBLE + homeworkDialogFullScreenExit.visibility = if (isHomeworkFullscreen) VISIBLE else GONE + homeworkDialogFullScreen.setOnClickListener { + homeworkDialogFullScreen.visibility = GONE + homeworkDialogFullScreenExit.visibility = VISIBLE + onFullScreenClickListener() + } + homeworkDialogFullScreenExit.setOnClickListener { + homeworkDialogFullScreen.visibility = VISIBLE + homeworkDialogFullScreenExit.visibility = GONE + onFullScreenExitClickListener() } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt index 1f9bc881..93045a48 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt @@ -1,19 +1,19 @@ package io.github.wulkanowy.ui.modules.homework.details import android.annotation.SuppressLint -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf +import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.databinding.DialogHomeworkBinding import io.github.wulkanowy.ui.base.BaseDialogFragment import io.github.wulkanowy.utils.openInternetBrowser -import io.github.wulkanowy.utils.serializable import javax.inject.Inject @AndroidEntryPoint @@ -25,9 +25,6 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew @Inject lateinit var detailsAdapter: HomeworkDetailsAdapter - override val homeworkDeleteSuccess: String - get() = getString(R.string.homework_delete_success) - private lateinit var homework: Homework companion object { @@ -35,20 +32,23 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew private const val ARGUMENT_KEY = "Item" fun newInstance(homework: Homework) = HomeworkDetailsDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to homework) + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - homework = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + homework = getSerializable(ARGUMENT_KEY) as Homework + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogHomeworkBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogHomeworkBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -64,20 +64,30 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew homeworkDialogClose.setOnClickListener { dismiss() } } + if (presenter.isHomeworkFullscreen) { + dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT) + } else { + dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) + } + with(binding.homeworkDialogRecycler) { layoutManager = LinearLayoutManager(context) adapter = detailsAdapter.apply { onAttachmentClickListener = { context.openInternetBrowser(it, ::showMessage) } - onDeleteClickListener = { homework -> presenter.deleteHomework(homework) } + onFullScreenClickListener = { + dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT) + presenter.isHomeworkFullscreen = true + } + onFullScreenExitClickListener = { + dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) + presenter.isHomeworkFullscreen = false + } + isHomeworkFullscreen = presenter.isHomeworkFullscreen homework = this@HomeworkDetailsDialog.homework } } } - override fun closeDialog() { - dismiss() - } - override fun updateMarkAsDoneLabel(isDone: Boolean) { binding.homeworkDialogRead.text = view?.context?.getString(if (isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt index 84933f06..ca6fc71e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt @@ -1,15 +1,15 @@ package io.github.wulkanowy.ui.modules.homework.details +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.onResourceError -import io.github.wulkanowy.data.onResourceSuccess import io.github.wulkanowy.data.repositories.HomeworkRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.flowWithResource +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -18,35 +18,35 @@ class HomeworkDetailsPresenter @Inject constructor( studentRepository: StudentRepository, private val homeworkRepository: HomeworkRepository, private val analytics: AnalyticsHelper, + private val preferencesRepository: PreferencesRepository ) : BasePresenter(errorHandler, studentRepository) { + var isHomeworkFullscreen + get() = preferencesRepository.isHomeworkFullscreen + set(value) { + preferencesRepository.isHomeworkFullscreen = value + } + override fun onAttachView(view: HomeworkDetailsView) { super.onAttachView(view) view.initView() Timber.i("Homework details view was initialized") } - fun deleteHomework(homework: Homework) { - resourceFlow { homeworkRepository.deleteHomework(homework) } - .logResourceStatus("homework delete") - .onResourceSuccess { - view?.run { - showMessage(homeworkDeleteSuccess) - closeDialog() + fun toggleDone(homework: Homework) { + flowWithResource { homeworkRepository.toggleDone(homework) }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Homework details update start") + Status.SUCCESS -> { + Timber.i("Homework details update: Success") + view?.updateMarkAsDoneLabel(homework.isDone) + analytics.logEvent("homework_mark_as_done") + } + Status.ERROR -> { + Timber.i("Homework details update result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceError(errorHandler::dispatch) - .launch("delete") - } - - fun toggleDone(homework: Homework) { - resourceFlow { homeworkRepository.toggleDone(homework) } - .logResourceStatus("homework details update") - .onResourceSuccess { - view?.updateMarkAsDoneLabel(homework.isDone) - analytics.logEvent("homework_mark_as_done") - } - .onResourceError(errorHandler::dispatch) - .launch("toggle") + }.launch("toggle") } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsView.kt index 4a47de43..697f2233 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsView.kt @@ -4,11 +4,7 @@ import io.github.wulkanowy.ui.base.BaseView interface HomeworkDetailsView : BaseView { - val homeworkDeleteSuccess: String - fun initView() - fun closeDialog() - fun updateMarkAsDoneLabel(isDone: Boolean) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt index c17c92ef..8d96a498 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt @@ -2,28 +2,20 @@ package io.github.wulkanowy.ui.modules.login import android.content.Context import android.content.Intent -import android.content.pm.PackageManager -import android.os.Build.VERSION_CODES.TIRAMISU import android.os.Bundle import android.view.MenuItem -import androidx.core.content.ContextCompat -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE -import androidx.fragment.app.commit import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.ActivityLoginBinding import io.github.wulkanowy.ui.base.BaseActivity +import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment 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.setOnSelectPageListener import javax.inject.Inject @AndroidEntryPoint @@ -32,16 +24,18 @@ class LoginActivity : BaseActivity(), Logi @Inject override lateinit var presenter: LoginPresenter + private val loginAdapter = BaseFragmentPagerAdapter(supportFragmentManager) + @Inject lateinit var updateHelper: UpdateHelper - @Inject - lateinit var appInfo: AppInfo - companion object { + fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java) } + override val currentViewIndex get() = binding.loginViewpager.currentItem + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(ActivityLoginBinding.inflate(layoutInflater).apply { binding = this }.root) @@ -51,68 +45,6 @@ class LoginActivity : BaseActivity(), Logi presenter.onAttachView(this) updateHelper.checkAndInstallUpdates(this) - - if (savedInstanceState == null) { - openFragment(LoginFormFragment.newInstance(), clearBackStack = true) - } - } - - override fun initView() { - with(requireNotNull(supportActionBar)) { - setDisplayHomeAsUpEnabled(true) - setDisplayShowTitleEnabled(false) - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) onBackPressedDispatcher.onBackPressed() - return true - } - - fun showActionBar(show: Boolean) { - supportActionBar?.run { if (show) show() else hide() } - } - - fun navigateToSymbolFragment(loginData: LoginData) { - openFragment(LoginSymbolFragment.newInstance(loginData)) - } - - fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) { - openFragment(LoginStudentSelectFragment.newInstance(loginData, registerUser)) - } - - fun navigateToNotifications() { - val isNotificationsPermissionRequired = appInfo.systemVersion >= TIRAMISU - val isPermissionGranted = ContextCompat.checkSelfPermission( - this, "android.permission.POST_NOTIFICATIONS" - ) == PackageManager.PERMISSION_GRANTED - - if (isNotificationsPermissionRequired && !isPermissionGranted) { - openFragment(NotificationsFragment.newInstance(), clearBackStack = true) - } else navigateToFinish() - } - - fun navigateToFinish() { - startActivity(MainActivity.getStartIntent(this)) - finish() - } - - fun onAdvancedLoginClick() { - openFragment(LoginAdvancedFragment.newInstance()) - } - - fun onRecoverClick() { - openFragment(LoginRecoverFragment.newInstance()) - } - - private fun openFragment(fragment: Fragment, clearBackStack: Boolean = false) { - supportFragmentManager.popBackStack(fragment::class.java.name, POP_BACK_STACK_INCLUSIVE) - - supportFragmentManager.commit { - replace(R.id.loginContainer, fragment) - setReorderingAllowed(true) - if (!clearBackStack) addToBackStack(fragment::class.java.name) - } } override fun onResume() { @@ -121,10 +53,82 @@ class LoginActivity : BaseActivity(), Logi } //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) } + + override fun initView() { + with(requireNotNull(supportActionBar)) { + setDisplayHomeAsUpEnabled(true) + setDisplayShowTitleEnabled(false) + } + + with(loginAdapter) { + containerId = binding.loginViewpager.id + addFragments( + listOf( + LoginFormFragment.newInstance(), + LoginSymbolFragment.newInstance(), + LoginStudentSelectFragment.newInstance(), + LoginAdvancedFragment.newInstance(), + LoginRecoverFragment.newInstance() + ) + ) + } + + with(binding.loginViewpager) { + offscreenPageLimit = 2 + adapter = loginAdapter + setOnSelectPageListener(presenter::onViewSelected) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) onBackPressed() + return true + } + + override fun switchView(index: Int) { + binding.loginViewpager.setCurrentItem(index, false) + } + + override fun showActionBar(show: Boolean) { + supportActionBar?.run { if (show) show() else hide() } + } + + override fun onBackPressed() { + presenter.onBackPressed { super.onBackPressed() } + } + + override fun notifyInitSymbolFragment(loginData: Triple) { + (loginAdapter.getFragmentInstance(1) as? LoginSymbolFragment)?.onParentInitSymbolFragment( + loginData + ) + } + + override fun notifyInitStudentSelectFragment(studentsWithSemesters: List) { + (loginAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment) + ?.onParentInitStudentSelectFragment(studentsWithSemesters) + } + + fun onFormFragmentAccountLogged( + studentsWithSemesters: List, + loginData: Triple + ) { + presenter.onFormViewAccountLogged(studentsWithSemesters, loginData) + } + + fun onSymbolFragmentAccountLogged(studentsWithSemesters: List) { + presenter.onSymbolViewAccountLogged(studentsWithSemesters) + } + + fun onAdvancedLoginClick() { + presenter.onAdvancedLoginClick() + } + + fun onRecoverClick() { + presenter.onRecoverClick() + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt deleted file mode 100644 index 2c11bb6d..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.ui.modules.login - -import java.io.Serializable - -data class LoginData( - val login: String, - val password: String, - val baseUrl: String, - val domainSuffix: String, - val symbol: String?, -) : Serializable diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt index 4f709438..ed456324 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt @@ -1,24 +1,19 @@ package io.github.wulkanowy.ui.modules.login -import android.content.Context +import android.content.res.Resources import android.database.sqlite.SQLiteConstraintException -import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R -import io.github.wulkanowy.sdk.hebe.exception.InvalidPinException -import io.github.wulkanowy.sdk.hebe.exception.InvalidTokenException -import io.github.wulkanowy.sdk.hebe.exception.TokenDeadException -import io.github.wulkanowy.sdk.hebe.exception.UnknownTokenException +import io.github.wulkanowy.sdk.mobile.exception.InvalidPinException +import io.github.wulkanowy.sdk.mobile.exception.InvalidSymbolException +import io.github.wulkanowy.sdk.mobile.exception.InvalidTokenException +import io.github.wulkanowy.sdk.mobile.exception.TokenDeadException import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject -import io.github.wulkanowy.sdk.hebe.exception.InvalidSymbolException as InvalidHebeSymbolException -import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException as InvalidScrapperSymbolException -class LoginErrorHandler @Inject constructor( - @ApplicationContext context: Context, -) : ErrorHandler(context) { +class LoginErrorHandler @Inject constructor(resources: Resources) : ErrorHandler(resources) { - var onBadCredentials: (String?) -> Unit = {} + var onBadCredentials: () -> Unit = {} var onInvalidToken: (String) -> Unit = {} @@ -29,16 +24,13 @@ class LoginErrorHandler @Inject constructor( var onStudentDuplicate: (String) -> Unit = {} override fun proceed(error: Throwable) { - val resources = context.resources when (error) { - is BadCredentialsException -> onBadCredentials(error.message) + is BadCredentialsException -> onBadCredentials() is SQLiteConstraintException -> onStudentDuplicate(resources.getString(R.string.login_duplicate_student)) is TokenDeadException -> onInvalidToken(resources.getString(R.string.login_expired_token)) - is UnknownTokenException, is InvalidTokenException -> onInvalidToken(resources.getString(R.string.login_invalid_token)) is InvalidPinException -> onInvalidPin(resources.getString(R.string.login_invalid_pin)) - is InvalidScrapperSymbolException, - is InvalidHebeSymbolException -> onInvalidSymbol(resources.getString(R.string.login_invalid_symbol)) + is InvalidSymbolException -> onInvalidSymbol(resources.getString(R.string.login_invalid_symbol)) else -> super.proceed(error) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt index 9031cb8a..aa1e7ece 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.login +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler @@ -13,7 +14,59 @@ class LoginPresenter @Inject constructor( override fun onAttachView(view: LoginView) { super.onAttachView(view) - view.initView() + with(view) { + initView() + showActionBar(false) + } Timber.i("Login view was initialized") } + + fun onFormViewAccountLogged(studentsWithSemesters: List, loginData: Triple) { + view?.apply { + if (studentsWithSemesters.isEmpty()) { + Timber.i("Switch to symbol form") + notifyInitSymbolFragment(loginData) + switchView(1) + } else { + Timber.i("Switch to student select") + notifyInitStudentSelectFragment(studentsWithSemesters) + switchView(2) + } + } + } + + fun onSymbolViewAccountLogged(studentsWithSemesters: List) { + view?.apply { + Timber.i("Switch to student select") + notifyInitStudentSelectFragment(studentsWithSemesters) + switchView(2) + } + } + + fun onAdvancedLoginClick() { + view?.switchView(3) + } + + fun onRecoverClick() { + view?.switchView(4) + } + + fun onViewSelected(index: Int) { + view?.apply { + when (index) { + 0 -> showActionBar(false) + 1, 2, 3, 4 -> showActionBar(true) + } + } + } + + fun onBackPressed(default: () -> Unit) { + Timber.i("Back pressed in login view") + view?.apply { + when (currentViewIndex) { + 1, 2, 3, 4 -> switchView(0) + else -> default() + } + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt index a0949e6d..2a5cf316 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt @@ -1,8 +1,19 @@ package io.github.wulkanowy.ui.modules.login +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView interface LoginView : BaseView { + val currentViewIndex: Int + fun initView() + + fun switchView(index: Int) + + fun showActionBar(show: Boolean) + + fun notifyInitSymbolFragment(loginData: Triple) + + fun notifyInitStudentSelectFragment(studentsWithSemesters: List) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt index 13d2c14a..9231914c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt @@ -5,16 +5,14 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.widget.ArrayAdapter -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.pojos.RegisterUser +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.FragmentLoginAdvancedBinding import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.login.LoginData import io.github.wulkanowy.ui.modules.login.form.LoginSymbolAdapter import io.github.wulkanowy.utils.hideSoftInput import io.github.wulkanowy.utils.setOnEditorDoneSignIn @@ -35,9 +33,9 @@ class LoginAdvancedFragment : override val formLoginType: String get() = when (binding.loginTypeSwitch.checkedRadioButtonId) { - R.id.loginTypeApi -> Sdk.Mode.HEBE.name - R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER.name - else -> Sdk.Mode.HYBRID.name + R.id.loginTypeApi -> "API" + R.id.loginTypeScrapper -> "SCRAPPER" + else -> "HYBRID" } override val formUsernameValue: String @@ -53,15 +51,10 @@ class LoginAdvancedFragment : private lateinit var hostSymbols: Array override val formHostValue: String - get() = hostValues.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) - .orEmpty() - - override val formDomainSuffix: String - get() = binding.loginFormDomainSuffix.text.toString().trim() + get() = hostValues.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())).orEmpty() override val formHostSymbol: String - get() = hostSymbols.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) - .orEmpty() + get() = hostSymbols.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())).orEmpty() override val formPinValue: String get() = binding.loginFormPin.text.toString().trim() @@ -85,8 +78,6 @@ class LoginAdvancedFragment : } override fun initView() { - (requireActivity() as LoginActivity).showActionBar(true) - hostKeys = resources.getStringArray(R.array.hosts_keys) hostValues = resources.getStringArray(R.array.hosts_values) hostSymbols = resources.getStringArray(R.array.hosts_symbols) @@ -101,62 +92,39 @@ class LoginAdvancedFragment : loginFormSignIn.setOnClickListener { presenter.onSignInClick() } loginTypeSwitch.setOnCheckedChangeListener { _, checkedId -> - presenter.onLoginModeSelected( - when (checkedId) { - R.id.loginTypeApi -> Sdk.Mode.HEBE - R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER - else -> Sdk.Mode.HYBRID - } - ) + presenter.onLoginModeSelected(when (checkedId) { + R.id.loginTypeApi -> Sdk.Mode.API + R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER + else -> Sdk.Mode.HYBRID + }) } loginFormPin.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() } loginFormPass.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() } - loginFormSymbol.setAdapter( - ArrayAdapter( - requireContext(), - android.R.layout.simple_list_item_1, - resources.getStringArray(R.array.symbols_values) - ) - ) + loginFormSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values))) } with(binding.loginFormHost) { setText(hostKeys.getOrNull(0).orEmpty()) - setAdapter( - LoginSymbolAdapter( - context, - R.layout.support_simple_spinner_dropdown_item, - hostKeys - ) - ) + setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys)) setOnClickListener { if (binding.loginFormContainer.visibility == GONE) dismissDropDown() } } } override fun showMobileApiWarningMessage() { - binding.loginFormAdvancedWarningInfo.text = - getString(R.string.login_advanced_warning_mobile_api) + binding.loginFormAdvancedWarningInfo.text = getString(R.string.login_advanced_warning_mobile_api) } override fun showScraperWarningMessage() { - binding.loginFormAdvancedWarningInfo.text = - getString(R.string.login_advanced_warning_scraper) + binding.loginFormAdvancedWarningInfo.text = getString(R.string.login_advanced_warning_scraper) } override fun showHybridWarningMessage() { - binding.loginFormAdvancedWarningInfo.text = - getString(R.string.login_advanced_warning_hybrid) + binding.loginFormAdvancedWarningInfo.text = getString(R.string.login_advanced_warning_hybrid) } - override fun setDefaultCredentials( - username: String, - pass: String, - symbol: String, - token: String, - pin: String - ) { + override fun setDefaultCredentials(username: String, pass: String, symbol: String, token: String, pin: String) { with(binding) { loginFormUsername.setText(username) loginFormPass.setText(pass) @@ -177,7 +145,7 @@ class LoginAdvancedFragment : override fun setErrorUsernameRequired() { with(binding.loginFormUsernameLayout) { requestFocus() - error = getString(R.string.error_field_required) + error = getString(R.string.login_field_required) } } @@ -198,7 +166,7 @@ class LoginAdvancedFragment : override fun setErrorPassRequired(focus: Boolean) { with(binding.loginFormPassLayout) { if (focus) requestFocus() - error = getString(R.string.error_field_required) + error = getString(R.string.login_field_required) } } @@ -209,17 +177,17 @@ class LoginAdvancedFragment : } } - override fun setErrorPassIncorrect(message: String?) { + override fun setErrorPassIncorrect() { with(binding.loginFormPassLayout) { requestFocus() - error = message ?: getString(R.string.login_incorrect_password_default) + error = getString(R.string.login_incorrect_password) } } override fun setErrorPinRequired() { with(binding.loginFormPinLayout) { requestFocus() - error = getString(R.string.error_field_required) + error = getString(R.string.login_field_required) } } @@ -233,7 +201,7 @@ class LoginAdvancedFragment : override fun setErrorSymbolRequired() { with(binding.loginFormSymbolLayout) { requestFocus() - error = getString(R.string.error_field_required) + error = getString(R.string.login_field_required) } } @@ -247,7 +215,7 @@ class LoginAdvancedFragment : override fun setErrorTokenRequired() { with(binding.loginFormTokenLayout) { requestFocus() - error = getString(R.string.error_field_required) + error = getString(R.string.login_field_required) } } @@ -283,7 +251,6 @@ class LoginAdvancedFragment : loginFormUsernameLayout.visibility = VISIBLE loginFormPassLayout.visibility = VISIBLE loginFormHostLayout.visibility = VISIBLE - loginFormDomainSuffixLayout.isVisible = true loginFormPinLayout.visibility = GONE loginFormSymbolLayout.visibility = VISIBLE loginFormTokenLayout.visibility = GONE @@ -295,7 +262,6 @@ class LoginAdvancedFragment : loginFormUsernameLayout.visibility = VISIBLE loginFormPassLayout.visibility = VISIBLE loginFormHostLayout.visibility = VISIBLE - loginFormDomainSuffixLayout.isVisible = true loginFormPinLayout.visibility = GONE loginFormSymbolLayout.visibility = VISIBLE loginFormTokenLayout.visibility = GONE @@ -307,7 +273,6 @@ class LoginAdvancedFragment : loginFormUsernameLayout.visibility = GONE loginFormPassLayout.visibility = GONE loginFormHostLayout.visibility = GONE - loginFormDomainSuffixLayout.isVisible = false loginFormPinLayout.visibility = VISIBLE loginFormSymbolLayout.visibility = VISIBLE loginFormTokenLayout.visibility = VISIBLE @@ -330,12 +295,12 @@ class LoginAdvancedFragment : binding.loginFormContainer.visibility = if (show) VISIBLE else GONE } - override fun navigateToSymbol(loginData: LoginData) { - (activity as? LoginActivity)?.navigateToSymbolFragment(loginData) - } - - override fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) { - (activity as? LoginActivity)?.navigateToStudentSelect(loginData, registerUser) + override fun notifyParentAccountLogged(studentsWithSemesters: List) { + (activity as? LoginActivity)?.onFormFragmentAccountLogged(studentsWithSemesters, Triple( + binding.loginFormUsername.text.toString(), + binding.loginFormPass.text.toString(), + resources.getStringArray(R.array.hosts_values)[1] + )) } override fun onResume() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt index a17ad003..891a6b0b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt @@ -1,17 +1,14 @@ package io.github.wulkanowy.ui.modules.login.advanced -import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.onResourceNotLoading -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.Status +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.sdk.scrapper.getNormalizedSymbol 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.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank import kotlinx.coroutines.flow.onEach import timber.log.Timber @@ -37,9 +34,9 @@ class LoginAdvancedPresenter @Inject constructor( } } - private fun onBadCredentials(message: String?) { + private fun onBadCredentials() { view?.run { - setErrorPassIncorrect(message) + setErrorPassIncorrect() showSoftKeyboard() Timber.i("Entered wrong username or password") } @@ -80,9 +77,7 @@ class LoginAdvancedPresenter @Inject constructor( clearPassError() clearUsernameError() if (formHostValue.contains("fakelog")) { - setDefaultCredentials( - "jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999" - ) + setDefaultCredentials("jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999") } setSymbol(formHostSymbol) updateUsernameLabel() @@ -92,16 +87,14 @@ class LoginAdvancedPresenter @Inject constructor( fun onLoginModeSelected(type: Sdk.Mode) { view?.run { when (type) { - Sdk.Mode.HEBE -> { + Sdk.Mode.API -> { showOnlyMobileApiModeInputs() showMobileApiWarningMessage() } - Sdk.Mode.SCRAPPER -> { showOnlyScrapperModeInputs() showScraperWarningMessage() } - Sdk.Mode.HYBRID -> { showOnlyHybridModeInputs() showHybridWarningMessage() @@ -133,75 +126,54 @@ class LoginAdvancedPresenter @Inject constructor( fun onSignInClick() { if (!validateCredentials()) return - resourceFlow { getStudentsAppropriatesToLoginType() } - .logResourceStatus("login") - .onEach { - when (it) { - is Resource.Loading -> view?.run { - hideSoftKeyboard() - showProgress(true) - showContent(false) - } - - is Resource.Success -> { - analytics.logEvent( - "registration_form", - "success" to true, - "scrapperBaseUrl" to view?.formHostValue.orEmpty(), - "error" to "No error" - ) - val loginData = LoginData( - login = view?.formUsernameValue.orEmpty().trim(), - password = view?.formPassValue.orEmpty().trim(), - baseUrl = view?.formHostValue.orEmpty().trim(), - domainSuffix = view?.formDomainSuffix.orEmpty().trim(), - symbol = view?.formSymbolValue.orEmpty().trim().getNormalizedSymbol(), - ) - when (it.data.symbols.size) { - 0 -> view?.navigateToSymbol(loginData) - else -> view?.navigateToStudentSelect( - loginData = loginData, - registerUser = it.data, - ) - } - } - - is Resource.Error -> { - analytics.logEvent( - "registration_form", - "success" to false, "students" to -1, - "error" to it.error.message.ifNullOrBlank { "No message" } - ) - loginErrorHandler.dispatch(it.error) - } + flowWithResource { getStudentsAppropriatesToLoginType() }.onEach { + when (it.status) { + Status.LOADING -> view?.run { + Timber.i("Login started") + hideSoftKeyboard() + showProgress(true) + showContent(false) } - }.onResourceNotLoading { - view?.apply { - showProgress(false) - showContent(true) + Status.SUCCESS -> { + Timber.i("Login result: Success") + analytics.logEvent("registration_form", + "success" to true, + "students" to it.data!!.size, + "error" to "No error" + ) + view?.notifyParentAccountLogged(it.data) } - }.launch("login") + Status.ERROR -> { + Timber.i("Login result: An exception occurred") + analytics.logEvent( + "registration_form", + "success" to false, "students" to -1, + "error" to it.error!!.message.ifNullOrBlank { "No message" } + ) + loginErrorHandler.dispatch(it.error) + } + } + }.afterLoading { + view?.apply { + showProgress(false) + showContent(true) + } + }.launch("login") } - private suspend fun getStudentsAppropriatesToLoginType(): RegisterUser { + private suspend fun getStudentsAppropriatesToLoginType(): List { val email = view?.formUsernameValue.orEmpty() val password = view?.formPassValue.orEmpty() val endpoint = view?.formHostValue.orEmpty() - val domainSuffix = view?.formDomainSuffix.orEmpty() val pin = view?.formPinValue.orEmpty() val symbol = view?.formSymbolValue.orEmpty() val token = view?.formTokenValue.orEmpty() return when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) { - Sdk.Mode.HEBE -> studentRepository.getStudentsApi(pin, symbol, token) - Sdk.Mode.SCRAPPER -> studentRepository.getUserSubjectsFromScrapper( - email, password, endpoint, domainSuffix, symbol - ) - - Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid( - email, password, endpoint, symbol - ) + Sdk.Mode.API -> studentRepository.getStudentsApi(pin, symbol, token) + Sdk.Mode.SCRAPPER -> studentRepository.getStudentsScrapper(email, password, endpoint, symbol) + Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid(email, password, endpoint, symbol) } } @@ -217,8 +189,8 @@ class LoginAdvancedPresenter @Inject constructor( var isCorrect = true - when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) { - Sdk.Mode.HEBE -> { + when (Sdk.Mode.valueOf(view?.formLoginType ?: "")) { + Sdk.Mode.API -> { if (pin.isEmpty()) { view?.setErrorPinRequired() isCorrect = false @@ -234,17 +206,17 @@ class LoginAdvancedPresenter @Inject constructor( isCorrect = false } } - Sdk.Mode.HYBRID, Sdk.Mode.SCRAPPER -> { if (login.isEmpty()) { view?.setErrorUsernameRequired() isCorrect = false } else { - if ("@" in login && "login" in host) { + if ("@" in login && "standard" !in host) { view?.setErrorLoginRequired() isCorrect = false } - if ("@" !in login && "email" in host) { + + if ("@" !in login && "standard" in host) { view?.setErrorEmailRequired() isCorrect = false } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt index afd33e3b..029a6b4d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt @@ -1,8 +1,7 @@ package io.github.wulkanowy.ui.modules.login.advanced -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView -import io.github.wulkanowy.ui.modules.login.LoginData interface LoginAdvancedView : BaseView { @@ -12,8 +11,6 @@ interface LoginAdvancedView : BaseView { val formHostValue: String - val formDomainSuffix: String - val formHostSymbol: String val formLoginType: String @@ -52,7 +49,7 @@ interface LoginAdvancedView : BaseView { fun setErrorPassInvalid(focus: Boolean) - fun setErrorPassIncorrect(message: String?) + fun setErrorPassIncorrect() fun clearUsernameError() @@ -72,9 +69,7 @@ interface LoginAdvancedView : BaseView { fun showContent(show: Boolean) - fun navigateToSymbol(loginData: LoginData) - - fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) + fun notifyParentAccountLogged(studentsWithSemesters: List) fun setErrorPinRequired() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt index 1085ff50..e383072e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt @@ -5,17 +5,19 @@ import android.os.Bundle import android.view.View import android.view.View.GONE import android.view.View.VISIBLE -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.pojos.RegisterUser -import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.FragmentLoginFormBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.login.LoginData -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.openEmailClient +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.setOnEditorDoneSignIn +import io.github.wulkanowy.utils.showSoftInput import javax.inject.Inject @AndroidEntryPoint @@ -28,9 +30,6 @@ class LoginFormFragment : BaseFragment(R.layout.fragme @Inject lateinit var appInfo: AppInfo - @Inject - lateinit var preferencesRepository: PreferencesRepository - companion object { fun newInstance() = LoginFormFragment() } @@ -42,15 +41,10 @@ class LoginFormFragment : BaseFragment(R.layout.fragme get() = binding.loginFormPass.text.toString() override val formHostValue: String - get() = hostValues.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) - .orEmpty() - - override val formDomainSuffix: String - get() = binding.loginFormDomainSuffix.text.toString() + get() = hostValues.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())).orEmpty() override val formHostSymbol: String - get() = hostSymbols.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) - .orEmpty() + get() = hostSymbols.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())).orEmpty() override val nicknameLabel: String get() = getString(R.string.login_nickname_hint) @@ -71,8 +65,6 @@ class LoginFormFragment : BaseFragment(R.layout.fragme } override fun initView() { - (requireActivity() as LoginActivity).showActionBar(false) - hostKeys = resources.getStringArray(R.array.hosts_keys) hostValues = resources.getStringArray(R.array.hosts_values) hostSymbols = resources.getStringArray(R.array.hosts_symbols) @@ -96,13 +88,7 @@ class LoginFormFragment : BaseFragment(R.layout.fragme with(binding.loginFormHost) { setText(hostKeys.getOrNull(0).orEmpty()) - setAdapter( - LoginSymbolAdapter( - context, - R.layout.support_simple_spinner_dropdown_item, - hostKeys - ) - ) + setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys)) setOnClickListener { if (binding.loginFormContainer.visibility == GONE) dismissDropDown() } } } @@ -128,7 +114,7 @@ class LoginFormFragment : BaseFragment(R.layout.fragme override fun setErrorUsernameRequired() { with(binding.loginFormUsernameLayout) { - error = getString(R.string.error_field_required) + error = getString(R.string.login_field_required) } } @@ -146,49 +132,34 @@ class LoginFormFragment : BaseFragment(R.layout.fragme override fun setErrorPassRequired(focus: Boolean) { with(binding.loginFormPassLayout) { - error = getString(R.string.error_field_required) - setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError)) + error = getString(R.string.login_field_required) } } override fun setErrorPassInvalid(focus: Boolean) { with(binding.loginFormPassLayout) { error = getString(R.string.login_invalid_password) - setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError)) } } - override fun setErrorPassIncorrect(message: String?) { - with(binding) { - loginFormUsernameLayout.error = " " - loginFormPassLayout.error = " " - loginFormPassLayout.setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError)) - loginFormHostLayout.error = " " - loginFormErrorBox.text = message ?: getString(R.string.login_incorrect_password_default) - loginFormErrorBox.isVisible = true + override fun setErrorPassIncorrect() { + with(binding.loginFormPassLayout) { + error = getString(R.string.login_incorrect_password) } } override fun setErrorEmailInvalid(domain: String) { with(binding.loginFormUsernameLayout) { - error = getString(R.string.login_invalid_custom_email, domain) + error = getString(R.string.login_invalid_custom_email,domain) } } override fun clearUsernameError() { binding.loginFormUsernameLayout.error = null - binding.loginFormErrorBox.isVisible = false } override fun clearPassError() { binding.loginFormPassLayout.error = null - binding.loginFormPassLayout.setEndIconTintList(null) - binding.loginFormErrorBox.isVisible = false - } - - override fun clearHostError() { - binding.loginFormHostLayout.error = null - binding.loginFormErrorBox.isVisible = false } override fun showSoftKeyboard() { @@ -207,37 +178,22 @@ class LoginFormFragment : BaseFragment(R.layout.fragme binding.loginFormContainer.visibility = if (show) VISIBLE else GONE } - override fun showDomainSuffixInput(show: Boolean) { - binding.loginFormDomainSuffixLayout.isVisible = show - } - - override fun showOtherOptionsButton(show: Boolean) { - binding.loginFormAdvancedButton.isVisible = show - } - @SuppressLint("SetTextI18n") override fun showVersion() { binding.loginFormVersion.text = "v${appInfo.versionName}" } - override fun showContact(show: Boolean) { - binding.loginFormContact.visibility = if (show) VISIBLE else GONE - binding.loginFormRecoverLink.visibility = if (show) GONE else VISIBLE + override fun notifyParentAccountLogged(studentsWithSemesters: List, loginData: Triple) { + (activity as? LoginActivity)?.onFormFragmentAccountLogged(studentsWithSemesters, loginData) } override fun openPrivacyPolicyPage() { - context?.openInternetBrowser( - "https://wulkanowy.github.io/polityka-prywatnosci.html", - ::showMessage - ) + context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage) } - override fun navigateToSymbol(loginData: LoginData) { - (activity as? LoginActivity)?.navigateToSymbolFragment(loginData) - } - - override fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) { - (activity as? LoginActivity)?.navigateToStudentSelect(loginData, registerUser) + override fun showContact(show: Boolean) { + binding.loginFormContact.visibility = if (show) VISIBLE else GONE + binding.loginFormRecoverLink.visibility = if (show) GONE else VISIBLE } override fun openAdvancedLogin() { @@ -254,16 +210,12 @@ class LoginFormFragment : BaseFragment(R.layout.fragme } override fun openFaqPage() { - context?.openInternetBrowser( - "https://wulkanowy.github.io/czesto-zadawane-pytania/dlaczego-nie-moge-sie-zalogowac", - ::showMessage - ) + context?.openInternetBrowser("https://wulkanowy.github.io/czesto-zadawane-pytania/dlaczego-nie-moge-sie-zalogowac", ::showMessage) } override fun onResume() { super.onResume() presenter.updateUsernameLabel() - presenter.updateCustomDomainSuffixVisibility() } override fun openEmail(lastError: String) { @@ -271,13 +223,11 @@ class LoginFormFragment : BaseFragment(R.layout.fragme 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, + body = requireContext().getString(R.string.login_email_text, "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), - "${appInfo.versionName}-${appInfo.buildFlavor}", + appInfo.versionName, "$formHostValue/$formHostSymbol", - preferencesRepository.installationId, lastError ) ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt index 85f42841..99dcf1bb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt @@ -1,19 +1,15 @@ package io.github.wulkanowy.ui.modules.login.form import androidx.core.net.toUri -import io.github.wulkanowy.data.logResourceStatus -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.Status import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow 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.utils.AnalyticsHelper -import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank +import kotlinx.coroutines.flow.onEach import timber.log.Timber import java.net.URL import javax.inject.Inject @@ -21,7 +17,6 @@ import javax.inject.Inject class LoginFormPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val appInfo: AppInfo, private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { @@ -32,11 +27,10 @@ class LoginFormPresenter @Inject constructor( view.run { initView() showContact(false) - showOtherOptionsButton(appInfo.isDebug) showVersion() loginErrorHandler.onBadCredentials = { - setErrorPassIncorrect(it.takeIf { !it.isNullOrBlank() }) + setErrorPassIncorrect() showSoftKeyboard() Timber.i("Entered wrong username or password") } @@ -55,23 +49,13 @@ class LoginFormPresenter @Inject constructor( view?.apply { clearPassError() clearUsernameError() - clearHostError() if (formHostValue.contains("fakelog")) { setCredentials("jan@fakelog.cf", "jan123") - } else if (formUsernameValue == "jan@fakelog.cf" && formPassValue == "jan123") { - setCredentials("", "") } - updateCustomDomainSuffixVisibility() updateUsernameLabel() } } - fun updateCustomDomainSuffixVisibility() { - view?.run { - showDomainSuffixInput("customSuffix" in formHostValue) - } - } - fun updateUsernameLabel() { view?.run { setUsernameLabel(if ("email" !in formHostValue) nicknameLabel else emailLabel) @@ -87,14 +71,11 @@ class LoginFormPresenter @Inject constructor( val username = view?.formUsernameValue.orEmpty().trim() if ("@" in username && "@vulcan" !in username) { - val hosts = view?.getHostsValues().orEmpty().associateBy { it.toUri().host } + val hosts = view?.getHostsValues().orEmpty().map { it.toUri().host to it }.toMap() val usernameHost = username.substringAfter("@") hosts[usernameHost]?.let { - view?.run { - setHost(it) - clearHostError() - } + view?.setHost(it) } } } @@ -103,59 +84,55 @@ class LoginFormPresenter @Inject constructor( 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 symbol = view?.formHostSymbol.orEmpty().trim() if (!validateCredentials(email, password, host)) return - resourceFlow { - studentRepository.getUserSubjectsFromScrapper( - email = email, - password = password, - scrapperBaseUrl = host, - domainSuffix = domainSuffix, - symbol = symbol + flowWithResource { + studentRepository.getStudentsScrapper( + email, + password, + host, + symbol ) - } - .logResourceStatus("login") - .onResourceLoading { - view?.run { + }.onEach { + when (it.status) { + Status.LOADING -> view?.run { + Timber.i("Login started") hideSoftKeyboard() showProgress(true) showContent(false) } - } - .onResourceSuccess { - val loginData = LoginData(email, password, host, domainSuffix, symbol) - when (it.symbols.size) { - 0 -> view?.navigateToSymbol(loginData) - else -> view?.navigateToStudentSelect(loginData, it) + Status.SUCCESS -> { + Timber.i("Login result: Success") + analytics.logEvent( + "registration_form", + "success" to true, + "students" to it.data!!.size, + "scrapperBaseUrl" to host, + "error" to "No error" + ) + view?.notifyParentAccountLogged(it.data, Triple(email, password, host)) } - analytics.logEvent( - "registration_form", - "success" to true, - "scrapperBaseUrl" to host, - "error" to "No error" - ) - } - .onResourceNotLoading { - view?.apply { - showProgress(false) - showContent(true) + Status.ERROR -> { + Timber.i("Login result: An exception occurred") + analytics.logEvent( + "registration_form", + "success" to false, + "students" to -1, + "scrapperBaseUrl" to host, + "error" to it.error!!.message.ifNullOrBlank { "No message" }) + loginErrorHandler.dispatch(it.error) + lastError = it.error + view?.showContact(true) } } - .onResourceError { - loginErrorHandler.dispatch(it) - lastError = it - view?.showContact(true) - analytics.logEvent( - "registration_form", - "success" to false, - "scrapperBaseUrl" to host, - "error" to it.message.ifNullOrBlank { "No message" } - ) + }.afterLoading { + view?.apply { + showProgress(false) + showContent(true) } - .launch("login") + }.launch("login") } fun onFaqClick() { @@ -188,7 +165,7 @@ class LoginFormPresenter @Inject constructor( if ("@" in login && "||" !in login && "login" !in host && "email" !in host) { val emailHost = login.substringAfter("@") val emailDomain = URL(host).host - if (!emailHost.equals(emailDomain, true)) { + if (emailHost != emailDomain) { view?.setErrorEmailInvalid(domain = emailDomain) isCorrect = false } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt index 5fb26062..079629ef 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt @@ -1,8 +1,7 @@ package io.github.wulkanowy.ui.modules.login.form -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView -import io.github.wulkanowy.ui.modules.login.LoginData interface LoginFormView : BaseView { @@ -14,8 +13,6 @@ interface LoginFormView : BaseView { val formHostValue: String - val formDomainSuffix: String - val formHostSymbol: String val nicknameLabel: String @@ -40,7 +37,7 @@ interface LoginFormView : BaseView { fun setErrorPassInvalid(focus: Boolean) - fun setErrorPassIncorrect(message: String?) + fun setErrorPassIncorrect() fun setErrorEmailInvalid(domain: String) @@ -48,8 +45,6 @@ interface LoginFormView : BaseView { fun clearPassError() - fun clearHostError() - fun showSoftKeyboard() fun hideSoftKeyboard() @@ -58,15 +53,9 @@ interface LoginFormView : BaseView { fun showContent(show: Boolean) - fun showDomainSuffixInput(show: Boolean) - - fun showOtherOptionsButton(show: Boolean) - fun showVersion() - fun navigateToSymbol(loginData: LoginData) - - fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) + fun notifyParentAccountLogged(studentsWithSemesters: List, loginData: Triple) fun openPrivacyPolicyPage() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt index b9afba98..2e2f9f5c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt @@ -6,13 +6,13 @@ import android.os.Bundle import android.view.View import android.view.View.GONE import android.view.View.VISIBLE -import android.webkit.* +import android.webkit.JavascriptInterface +import android.webkit.WebView +import android.webkit.WebViewClient import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged -import com.yariksoffice.lingver.Lingver import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.FragmentLoginRecoverBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.login.LoginActivity @@ -32,12 +32,6 @@ class LoginRecoverFragment : @Inject lateinit var presenter: LoginRecoverPresenter - @Inject - lateinit var lingver: Lingver - - @Inject - lateinit var preferencesRepository: PreferencesRepository - companion object { fun newInstance() = LoginRecoverFragment() } @@ -70,23 +64,11 @@ class LoginRecoverFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - restoreCorrectLocale() _binding = FragmentLoginRecoverBinding.bind(view) presenter.onAttachView(this) } - // https://issuetracker.google.com/issues/37113860 - private fun restoreCorrectLocale() { - if (preferencesRepository.appLanguage == "system") { - lingver.setFollowSystemLocale(requireContext()) - } else { - lingver.setLocale(requireContext(), lingver.getLocale()) - } - } - override fun initView() { - (requireActivity() as LoginActivity).showActionBar(true) - hostKeys = resources.getStringArray(R.array.hosts_keys) hostValues = resources.getStringArray(R.array.hosts_values) hostSymbols = resources.getStringArray(R.array.hosts_symbols) @@ -98,7 +80,7 @@ class LoginRecoverFragment : loginRecoverButton.setOnClickListener { presenter.onRecoverClick() } loginRecoverErrorRetry.setOnClickListener { presenter.onRecoverClick() } loginRecoverErrorDetails.setOnClickListener { presenter.onDetailsClick() } - loginRecoverLogin.setOnClickListener { (activity as LoginActivity).onBackPressedDispatcher.onBackPressed() } + loginRecoverLogin.setOnClickListener { (activity as LoginActivity).switchView(0) } } with(bindingLocal.loginRecoverHost) { @@ -117,7 +99,7 @@ class LoginRecoverFragment : override fun setErrorNameRequired() { with(bindingLocal.loginRecoverNameLayout) { requestFocus() - error = getString(R.string.error_field_required) + error = getString(R.string.login_field_required) } } @@ -204,9 +186,10 @@ class LoginRecoverFragment : } override fun onReceivedError( - view: WebView?, - request: WebResourceRequest?, - error: WebResourceError? + view: WebView, + errorCode: Int, + description: String, + failingUrl: String ) { recoverWebViewSuccess = false } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt index 3d049301..271e8a8a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt @@ -1,12 +1,12 @@ package io.github.wulkanowy.ui.modules.login.recover -import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.onResourceNotLoading +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.RecoverRepository import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank import kotlinx.coroutines.flow.onEach import timber.log.Timber @@ -57,28 +57,24 @@ class LoginRecoverPresenter @Inject constructor( if (!validateInput(username, host)) return - resourceFlow { - recoverRepository.getReCaptchaSiteKey( - host, - symbol.ifBlank { "Default" }) - }.onEach { - when (it) { - is Resource.Loading -> view?.run { + flowWithResource { recoverRepository.getReCaptchaSiteKey(host, symbol.ifBlank { "Default" }) }.onEach { + when (it.status) { + Status.LOADING -> view?.run { hideSoftKeyboard() showRecoverForm(false) showProgress(true) showErrorView(false) showCaptcha(false) } - is Resource.Success -> view?.run { - loadReCaptcha(url = it.data.first, siteKey = it.data.second) + Status.SUCCESS -> view?.run { + loadReCaptcha(url = it.data!!.first, siteKey = it.data.second) showProgress(false) showErrorView(false) showCaptcha(true) } - is Resource.Error -> { + Status.ERROR -> { Timber.i("Obtain captcha site key result: An exception occurred") - errorHandler.dispatch(it.error) + errorHandler.dispatch(it.error!!) } } }.launch("captcha") @@ -105,43 +101,26 @@ class LoginRecoverPresenter @Inject constructor( val host = view?.recoverHostValue.orEmpty() val symbol = view?.formHostSymbol.ifNullOrBlank { "Default" } - resourceFlow { - recoverRepository.sendRecoverRequest( - host, - symbol, - username, - reCaptchaResponse - ) - }.onEach { - when (it) { - is Resource.Loading -> view?.run { + flowWithResource { recoverRepository.sendRecoverRequest(host, symbol, username, reCaptchaResponse) }.onEach { + when (it.status) { + Status.LOADING -> view?.run { showProgress(true) showRecoverForm(false) showCaptcha(false) } - is Resource.Success -> view?.run { + Status.SUCCESS -> view?.run { showSuccessView(true) - setSuccessTitle(it.data.substringBefore(". ")) + setSuccessTitle(it.data!!.substringBefore(". ")) setSuccessMessage(it.data.substringAfter(". ")) - analytics.logEvent( - "account_recover", - "register" to host, - "symbol" to symbol, - "success" to true - ) + analytics.logEvent("account_recover", "register" to host, "symbol" to symbol, "success" to true) } - is Resource.Error -> { + Status.ERROR -> { Timber.i("Send recover request result: An exception occurred") - errorHandler.dispatch(it.error) - analytics.logEvent( - "account_recover", - "register" to host, - "symbol" to symbol, - "success" to false - ) + errorHandler.dispatch(it.error!!) + analytics.logEvent("account_recover", "register" to host, "symbol" to symbol, "success" to false) } } - }.onResourceNotLoading { + }.afterLoading { view?.showProgress(false) }.launch("verified") } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt index 28686d62..8619369d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt @@ -1,16 +1,13 @@ package io.github.wulkanowy.ui.modules.login.recover -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext +import android.content.res.Resources import io.github.wulkanowy.sdk.scrapper.exception.InvalidCaptchaException import io.github.wulkanowy.sdk.scrapper.exception.InvalidEmailException import io.github.wulkanowy.sdk.scrapper.exception.NoAccountFoundException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject -class RecoverErrorHandler @Inject constructor( - @ApplicationContext context: Context, -) : ErrorHandler(context) { +class RecoverErrorHandler @Inject constructor(resources: Resources) : ErrorHandler(resources) { var onInvalidUsername: (String) -> Unit = {} @@ -18,8 +15,7 @@ class RecoverErrorHandler @Inject constructor( override fun proceed(error: Throwable) { when (error) { - is InvalidEmailException, - is NoAccountFoundException -> onInvalidUsername(error.localizedMessage.orEmpty()) + is InvalidEmailException, is NoAccountFoundException -> onInvalidUsername(error.localizedMessage.orEmpty()) is InvalidCaptchaException -> onInvalidCaptcha(error.localizedMessage.orEmpty(), error) else -> super.proceed(error) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt index e6d13182..c046c2ff 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt @@ -2,182 +2,65 @@ package io.github.wulkanowy.ui.modules.login.studentselect import android.annotation.SuppressLint import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.recyclerview.widget.DiffUtil.ItemCallback -import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView -import io.github.wulkanowy.R -import io.github.wulkanowy.databinding.* +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.ItemLoginStudentSelectBinding import javax.inject.Inject -@SuppressLint("SetTextI18n") class LoginStudentSelectAdapter @Inject constructor() : - ListAdapter(Differ) { + RecyclerView.Adapter() { - override fun getItemViewType(position: Int): Int = getItem(position).type.ordinal + private val checkedList = mutableMapOf() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val inflater = LayoutInflater.from(parent.context) - return when (LoginStudentSelectItemType.values()[viewType]) { - LoginStudentSelectItemType.EMPTY_SYMBOLS_HEADER -> EmptySymbolsHeaderViewHolder( - ItemLoginStudentSelectEmptySymbolHeaderBinding.inflate(inflater, parent, false), - ) - LoginStudentSelectItemType.SYMBOL_HEADER -> SymbolsHeaderViewHolder( - ItemLoginStudentSelectHeaderSymbolBinding.inflate(inflater, parent, false) - ) - LoginStudentSelectItemType.SCHOOL_HEADER -> SchoolHeaderViewHolder( - ItemLoginStudentSelectHeaderSchoolBinding.inflate(inflater, parent, false) - ) - LoginStudentSelectItemType.STUDENT -> StudentViewHolder( - ItemLoginStudentSelectStudentBinding.inflate(inflater, parent, false) - ) - LoginStudentSelectItemType.HELP -> HelpViewHolder( - ItemLoginStudentSelectHelpBinding.inflate(inflater, parent, false) - ) + var items = emptyList>() + set(value) { + field = value + checkedList.clear() } - } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - when (holder) { - is EmptySymbolsHeaderViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.EmptySymbolsHeader) - is SymbolsHeaderViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.SymbolHeader) - is SchoolHeaderViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.SchoolHeader) - is StudentViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.Student) - is HelpViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.Help) - } - } + var onClickListener: (StudentWithSemesters, alreadySaved: Boolean) -> Unit = { _, _ -> } - private class EmptySymbolsHeaderViewHolder( - private val binding: ItemLoginStudentSelectEmptySymbolHeaderBinding, - ) : RecyclerView.ViewHolder(binding.root) { + override fun getItemCount() = items.size - fun bind(item: LoginStudentSelectItem.EmptySymbolsHeader) { - with(binding) { - loginStudentSelectEmptySymbolChevron.rotation = if (item.isExpanded) 270f else 90f - root.setOnClickListener { item.onClick() } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemLoginStudentSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val (studentAndSemesters, alreadySaved) = items[position] + val student = studentAndSemesters.student + val semesters = studentAndSemesters.semesters + val diary = semesters.maxByOrNull { it.semesterId } + + with(holder.binding) { + loginItemName.text = "${student.studentName} ${diary?.diaryName.orEmpty()}" + loginItemSchool.text = student.schoolName + loginItemName.isEnabled = !alreadySaved + loginItemSchool.isEnabled = !alreadySaved + loginItemSignedIn.visibility = if (alreadySaved) View.VISIBLE else View.GONE + + with(loginItemCheck) { + isEnabled = !alreadySaved + keyListener = null + isChecked = checkedList[position] ?: false } - } - } - private class SymbolsHeaderViewHolder( - private val binding: ItemLoginStudentSelectHeaderSymbolBinding, - ) : RecyclerView.ViewHolder(binding.root) { - - fun bind(item: LoginStudentSelectItem.SymbolHeader) { - with(binding) { - loginStudentSelectHeaderSymbolValue.text = buildString { - append(root.context.getString(R.string.mobile_device_symbol)) - append(": ") - append(item.humanReadableName ?: item.symbol.symbol) - if (!item.humanReadableName.isNullOrBlank()) { - append(" (${item.symbol.symbol})") - } - } - loginStudentSelectHeaderSymbolUsername.text = item.symbol.userName - loginStudentSelectHeaderSymbolUsername.isVisible = item.symbol.userName.isNotBlank() - loginStudentSelectHeaderSymbolError.text = item.symbol.error?.message - loginStudentSelectHeaderSymbolError.isVisible = item.symbol.error != null - loginStudentSelectHeaderSymbolError.maxLines = when { - item.isErrorExpanded -> Int.MAX_VALUE - else -> 2 - } - - if (item.symbol.error != null) { - root.setOnClickListener { item.onClick(item.symbol) } - } else root.setOnClickListener(null) - } - } - } - - private class SchoolHeaderViewHolder( - private val binding: ItemLoginStudentSelectHeaderSchoolBinding, - ) : RecyclerView.ViewHolder(binding.root) { - - fun bind(item: LoginStudentSelectItem.SchoolHeader) { - with(binding) { - loginStudentSelectHeaderSchoolName.text = buildString { - append(item.unit.schoolName.trim()) - append(" (") - append(item.unit.schoolShortName) - append(")") - } - loginStudentSelectHeaderSchoolDetails.isVisible = item.unit.students.isEmpty() - loginStudentSelectHeaderSchoolError.text = item.unit.error?.message - loginStudentSelectHeaderSchoolError.isVisible = item.unit.error != null - loginStudentSelectHeaderSchoolError.maxLines = when { - item.isErrorExpanded -> Int.MAX_VALUE - else -> 2 - } - - if (item.unit.error != null) { - root.setOnClickListener { item.onClick(item.unit) } - } else root.setOnClickListener(null) - } - } - } - - private class StudentViewHolder( - private val binding: ItemLoginStudentSelectStudentBinding, - ) : RecyclerView.ViewHolder(binding.root) { - - fun bind(item: LoginStudentSelectItem.Student) { - val student = item.student - val semesters = student.semesters - val diary = semesters.maxByOrNull { it.semesterId } - - with(binding) { - loginItemName.text = "${student.studentName} ${student.studentSurname}" - loginItemName.isEnabled = item.isEnabled - loginItemSignedIn.text = if (!item.isEnabled) { - root.context.getString(R.string.login_signed_in) - } else diary?.diaryName + root.setOnClickListener { + onClickListener(studentAndSemesters, alreadySaved) with(loginItemCheck) { - keyListener = null - isEnabled = item.isEnabled - isChecked = item.isSelected || !item.isEnabled - } - - root.isEnabled = item.isEnabled - root.setOnClickListener { - item.onClick(item) + if (isEnabled) { + isChecked = !isChecked + checkedList[position] = isChecked + } } } } } - private class HelpViewHolder( - private val binding: ItemLoginStudentSelectHelpBinding, - ) : RecyclerView.ViewHolder(binding.root) { - - fun bind(item: LoginStudentSelectItem.Help) { - with(binding) { - loginStudentSelectHelpSymbol.isVisible = item.isSymbolButtonVisible - loginStudentSelectHelpSymbol.setOnClickListener { item.onEnterSymbolClick() } - loginStudentSelectHelpMail.setOnClickListener { item.onContactUsClick() } - loginStudentSelectHelpDiscord.setOnClickListener { item.onDiscordClick() } - } - } - } - - private object Differ : ItemCallback() { - - override fun areItemsTheSame( - oldItem: LoginStudentSelectItem, newItem: LoginStudentSelectItem - ): Boolean = when { - oldItem is LoginStudentSelectItem.EmptySymbolsHeader && newItem is LoginStudentSelectItem.EmptySymbolsHeader -> true - oldItem is LoginStudentSelectItem.SymbolHeader && newItem is LoginStudentSelectItem.SymbolHeader -> { - oldItem.symbol == newItem.symbol - } - oldItem is LoginStudentSelectItem.Student && newItem is LoginStudentSelectItem.Student -> { - oldItem.student == newItem.student - } - else -> oldItem == newItem - } - - override fun areContentsTheSame( - oldItem: LoginStudentSelectItem, newItem: LoginStudentSelectItem - ): Boolean = oldItem == newItem - } + class ItemViewHolder(val binding: ItemLoginStudentSelectBinding) : + RecyclerView.ViewHolder(binding.root) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt index c33d12fa..e71fc0f6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt @@ -2,20 +2,19 @@ package io.github.wulkanowy.ui.modules.login.studentselect import android.os.Bundle import android.view.View -import androidx.core.os.bundleOf -import androidx.core.view.isVisible +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.pojos.RegisterUser -import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.login.LoginData +import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.openEmailClient import io.github.wulkanowy.utils.openInternetBrowser -import io.github.wulkanowy.utils.serializable +import java.io.Serializable import javax.inject.Inject @AndroidEntryPoint @@ -32,76 +31,74 @@ class LoginStudentSelectFragment : @Inject lateinit var appInfo: AppInfo - @Inject - lateinit var preferencesRepository: PreferencesRepository - - private lateinit var symbolsNames: Array - private lateinit var symbolsValues: Array - - override val symbols: Map by lazy { - symbolsValues.zip(symbolsNames).toMap() - } - companion object { - private const val ARG_LOGIN = "LOGIN" - private const val ARG_STUDENTS = "STUDENTS" + const val SAVED_STUDENTS = "STUDENTS" - fun newInstance(loginData: LoginData, registerUser: RegisterUser) = - LoginStudentSelectFragment().apply { - arguments = bundleOf( - ARG_LOGIN to loginData, - ARG_STUDENTS to registerUser, - ) - } + fun newInstance() = LoginStudentSelectFragment() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentLoginStudentSelectBinding.bind(view) - - symbolsNames = resources.getStringArray(R.array.symbols) - symbolsValues = resources.getStringArray(R.array.symbols_values) - - presenter.onAttachView( - view = this, - loginData = requireArguments().serializable(ARG_LOGIN), - registerUser = requireArguments().serializable(ARG_STUDENTS), - ) + presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_STUDENTS)) } override fun initView() { - (requireActivity() as LoginActivity).showActionBar(true) + loginAdapter.onClickListener = presenter::onItemSelected with(binding) { loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() } - loginStudentSelectRecycler.adapter = loginAdapter + loginStudentSelectContactDiscord.setOnClickListener { presenter.onDiscordClick() } + loginStudentSelectContactEmail.setOnClickListener { presenter.onEmailClick() } + + with(loginStudentSelectRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = loginAdapter + } } } - override fun updateData(data: List) { - loginAdapter.submitList(data) + override fun updateData(data: List>) { + with(loginAdapter) { + items = data + notifyDataSetChanged() + } } - override fun navigateToSymbol(loginData: LoginData) { - (requireActivity() as LoginActivity).navigateToSymbolFragment(loginData) - } - - override fun navigateToNext() { - (requireActivity() as LoginActivity).navigateToNotifications() + override fun openMainView() { + activity?.let { startActivity(MainActivity.getStartIntent(context = it, clear = true)) } } override fun showProgress(show: Boolean) { - binding.loginStudentSelectProgress.isVisible = show + binding.loginStudentSelectProgress.visibility = if (show) VISIBLE else GONE } override fun showContent(show: Boolean) { - binding.loginStudentSelectContent.isVisible = show + binding.loginStudentSelectContent.visibility = if (show) VISIBLE else GONE } override fun enableSignIn(enable: Boolean) { binding.loginStudentSelectSignIn.isEnabled = enable } + fun onParentInitStudentSelectFragment(studentsWithSemesters: List) { + presenter.onParentInitStudentSelectView(studentsWithSemesters) + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putSerializable(SAVED_STUDENTS, presenter.students as Serializable) + } + + override fun showContact(show: Boolean) { + binding.loginStudentSelectContact.visibility = if (show) VISIBLE else GONE + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } + override fun openDiscordInvite() { context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) } @@ -111,20 +108,12 @@ class LoginStudentSelectFragment : 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}", + body = requireContext().getString(R.string.login_email_text, appInfo.systemModel, appInfo.systemVersion.toString(), - "${appInfo.versionName}-${appInfo.buildFlavor}", + appInfo.versionName, "Select users to log in", - preferencesRepository.installationId, lastError ) ) } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt deleted file mode 100644 index 1edc8e7b..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.studentselect - -import io.github.wulkanowy.data.pojos.RegisterStudent -import io.github.wulkanowy.data.pojos.RegisterSymbol -import io.github.wulkanowy.data.pojos.RegisterUnit - -sealed class LoginStudentSelectItem(val type: LoginStudentSelectItemType) { - - data class EmptySymbolsHeader( - val isExpanded: Boolean, - val onClick: () -> Unit, - ) : LoginStudentSelectItem(LoginStudentSelectItemType.EMPTY_SYMBOLS_HEADER) - - data class SymbolHeader( - val symbol: RegisterSymbol, - val humanReadableName: String?, - val isErrorExpanded: Boolean, - val onClick: (RegisterSymbol) -> Unit, - ) : LoginStudentSelectItem(LoginStudentSelectItemType.SYMBOL_HEADER) - - data class SchoolHeader( - val unit: RegisterUnit, - val isErrorExpanded: Boolean, - val onClick: (RegisterUnit) -> Unit, - ) : LoginStudentSelectItem(LoginStudentSelectItemType.SCHOOL_HEADER) - - data class Student( - val symbol: RegisterSymbol, - val unit: RegisterUnit, - val student: RegisterStudent, - val isEnabled: Boolean, - val isSelected: Boolean, - val onClick: (Student) -> Unit, - ) : LoginStudentSelectItem(LoginStudentSelectItemType.STUDENT) - - data class Help( - val onEnterSymbolClick: () -> Unit, - val onContactUsClick: () -> Unit, - val onDiscordClick: () -> Unit, - val isSymbolButtonVisible: Boolean, - ) : LoginStudentSelectItem(LoginStudentSelectItemType.HELP) -} - -enum class LoginStudentSelectItemType { - EMPTY_SYMBOLS_HEADER, - SYMBOL_HEADER, - SCHOOL_HEADER, - STUDENT, - HELP, -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt index 70862e82..c344bf44 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt @@ -1,57 +1,36 @@ package io.github.wulkanowy.ui.modules.login.studentselect -import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.dataOrNull +import io.github.wulkanowy.data.Status +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.mappers.mapToStudentWithSemesters -import io.github.wulkanowy.data.pojos.RegisterStudent -import io.github.wulkanowy.data.pojos.RegisterSymbol -import io.github.wulkanowy.data.pojos.RegisterUnit -import io.github.wulkanowy.data.pojos.RegisterUser import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow -import io.github.wulkanowy.sdk.scrapper.login.AccountPermissionException -import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException -import io.github.wulkanowy.services.sync.SyncManager 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.utils.AnalyticsHelper -import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank import kotlinx.coroutines.flow.onEach import timber.log.Timber +import java.io.Serializable import javax.inject.Inject class LoginStudentSelectPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val syncManager: SyncManager, - private val analytics: AnalyticsHelper, - private val appInfo: AppInfo, + private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { private var lastError: Throwable? = null - private lateinit var registerUser: RegisterUser - private lateinit var loginData: LoginData + var students = emptyList() - private lateinit var students: List - private var isEmptySymbolsExpanded = false - private var expandedSymbolError: RegisterSymbol? = null - private var expandedSchoolError: RegisterUnit? = null + private val selectedStudents = mutableListOf() - private val selectedStudents = mutableListOf() - - fun onAttachView( - view: LoginStudentSelectView, - loginData: LoginData, - registerUser: RegisterUser, - ) { + fun onAttachView(view: LoginStudentSelectView, students: Serializable?) { super.onAttachView(view) with(view) { initView() + showContact(false) enableSignIn(false) loginErrorHandler.onStudentDuplicate = { showMessage(it) @@ -59,171 +38,55 @@ class LoginStudentSelectPresenter @Inject constructor( } } - this.loginData = loginData - this.registerUser = registerUser - loadData() - } - - private fun loadData() { - resetSelectedState() - - resourceFlow { studentRepository.getSavedStudents(false) }.onEach { - students = it.dataOrNull.orEmpty() - when (it) { - is Resource.Loading -> Timber.d("Login student select students load started") - is Resource.Success -> refreshItems() - is Resource.Error -> { - errorHandler.dispatch(it.error) - lastError = it.error - refreshItems() - } - } - }.launch() - } - - private fun createItems(): List = buildList { - val notEmptySymbols = registerUser.symbols.filter { it.schools.isNotEmpty() } - val emptySymbols = registerUser.symbols.filter { it.schools.isEmpty() } - - if (emptySymbols.isNotEmpty() && notEmptySymbols.isNotEmpty() && emptySymbols.any { it.symbol == loginData.symbol }) { - add(createEmptySymbolItem(emptySymbols.first { it.symbol == loginData.symbol })) - } - - addAll(createNotEmptySymbolItems(notEmptySymbols, students)) - addAll(createEmptySymbolItems(emptySymbols, notEmptySymbols.isNotEmpty())) - - val helpItem = LoginStudentSelectItem.Help( - onEnterSymbolClick = ::onEnterSymbol, - onContactUsClick = ::onEmailClick, - onDiscordClick = ::onDiscordClick, - isSymbolButtonVisible = "login" !in loginData.baseUrl, - ) - add(helpItem) - } - - private fun createNotEmptySymbolItems( - notEmptySymbols: List, - students: List, - ) = buildList { - notEmptySymbols.forEach { registerSymbol -> - val symbolHeader = LoginStudentSelectItem.SymbolHeader( - symbol = registerSymbol, - humanReadableName = view?.symbols?.get(registerSymbol.symbol), - isErrorExpanded = expandedSymbolError == registerSymbol, - onClick = ::onSymbolItemClick, - ) - add(symbolHeader) - - registerSymbol.schools.forEach { registerUnit -> - val schoolHeader = LoginStudentSelectItem.SchoolHeader( - unit = registerUnit, - isErrorExpanded = expandedSchoolError == registerUnit, - onClick = ::onUnitItemClick, - ) - add(schoolHeader) - - registerUnit.students.forEach { - add(createStudentItem(it, registerSymbol, registerUnit, students)) - } - } + if (students is List<*> && students.isNotEmpty()) { + loadData(students.filterIsInstance()) } } - private fun createStudentItem( - student: RegisterStudent, - symbol: RegisterSymbol, - school: RegisterUnit, - students: List, - ) = LoginStudentSelectItem.Student( - symbol = symbol, - unit = school, - student = student, - onClick = ::onItemSelected, - isEnabled = students.none { - it.student.email == registerUser.login - && it.student.symbol == symbol.symbol - && it.student.studentId == student.studentId - && it.student.schoolSymbol == school.schoolId - && it.student.classId == student.classId - }, - isSelected = selectedStudents - .filter { it.symbol.symbol == symbol.symbol } - .filter { it.unit.schoolId == school.schoolId } - .filter { it.student.studentId == student.studentId } - .filter { it.student.classId == student.classId } - .size == 1, - ) - - private fun createEmptySymbolItems( - emptySymbols: List, - isNotEmptySymbolsExist: Boolean, - ) = buildList { - val filteredEmptySymbols = emptySymbols.filter { - it.error !is InvalidSymbolException - }.ifEmpty { emptySymbols.takeIf { !isNotEmptySymbolsExist }.orEmpty() } - - if (filteredEmptySymbols.isNotEmpty() && isNotEmptySymbolsExist) { - val emptyHeader = LoginStudentSelectItem.EmptySymbolsHeader( - isExpanded = isEmptySymbolsExpanded, - onClick = ::onEmptySymbolsToggle, - ) - add(emptyHeader) - if (isEmptySymbolsExpanded) { - filteredEmptySymbols.forEach { - add(createEmptySymbolItem(it)) - } - } - } - - if (filteredEmptySymbols.isNotEmpty() && !isNotEmptySymbolsExist) { - filteredEmptySymbols.forEach { - add(createEmptySymbolItem(it)) - } - } - } - - private fun createEmptySymbolItem(registerSymbol: RegisterSymbol) = - LoginStudentSelectItem.SymbolHeader( - symbol = registerSymbol, - humanReadableName = view?.symbols?.get(registerSymbol.symbol), - isErrorExpanded = expandedSymbolError == registerSymbol, - onClick = ::onSymbolItemClick, - ) - fun onSignIn() { registerStudents(selectedStudents) } - private fun onEmptySymbolsToggle() { - isEmptySymbolsExpanded = !isEmptySymbolsExpanded - - refreshItems() + fun onParentInitStudentSelectView(studentsWithSemesters: List) { + loadData(studentsWithSemesters) + if (studentsWithSemesters.size == 1) registerStudents(studentsWithSemesters) } - private fun onItemSelected(item: LoginStudentSelectItem.Student) { - if (!item.isEnabled) return + fun onItemSelected(studentWithSemester: StudentWithSemesters, alreadySaved: Boolean) { + if (alreadySaved) return selectedStudents - .removeAll { - it.student.studentId == item.student.studentId && - it.student.classId == item.student.classId && - it.unit.schoolId == item.unit.schoolId && - it.symbol.symbol == item.symbol.symbol - } - .let { if (!it) selectedStudents.add(item) } + .removeAll { it == studentWithSemester } + .let { if (!it) selectedStudents.add(studentWithSemester) } view?.enableSignIn(selectedStudents.isNotEmpty()) - refreshItems() } - private fun onSymbolItemClick(symbol: RegisterSymbol) { - expandedSymbolError = if (symbol != expandedSymbolError) symbol else null - refreshItems() + private fun compareStudents(a: Student, b: Student): Boolean { + return a.email == b.email + && a.symbol == b.symbol + && a.studentId == b.studentId + && a.schoolSymbol == b.schoolSymbol + && a.classId == b.classId } - private fun onUnitItemClick(unit: RegisterUnit) { - expandedSchoolError = if (unit != expandedSchoolError) unit else null - refreshItems() + private fun loadData(studentsWithSemesters: List) { + resetSelectedState() + this.students = studentsWithSemesters + + flowWithResource { studentRepository.getSavedStudents(false) }.onEach { + when (it.status) { + Status.LOADING -> Timber.d("Login student select students load started") + Status.SUCCESS -> view?.updateData(studentsWithSemesters.map { studentWithSemesters -> + studentWithSemesters to it.data!!.any { item -> compareStudents(studentWithSemesters.student, item.student) } + }) + Status.ERROR -> { + errorHandler.dispatch(it.error!!) + lastError = it.error + view?.updateData(studentsWithSemesters.map { student -> student to false }) + } + } + }.launch() } private fun resetSelectedState() { @@ -231,92 +94,54 @@ class LoginStudentSelectPresenter @Inject constructor( view?.enableSignIn(false) } - private fun refreshItems() { - view?.updateData(createItems()) - } - - private fun registerStudents(students: List) { - val studentsWithSemesters = students - .filterIsInstance().map { item -> - item.student.mapToStudentWithSemesters( - user = registerUser, - symbol = item.symbol, - scrapperDomainSuffix = loginData.domainSuffix, - unit = item.unit, - colors = appInfo.defaultColorsForAvatar, - ) - } - resourceFlow { studentRepository.saveStudents(studentsWithSemesters) } - .logResourceStatus("registration") - .onEach { - when (it) { - is Resource.Loading -> view?.run { - showProgress(true) - showContent(false) - } - is Resource.Success -> { - syncManager.startOneTimeSyncWorker(quiet = true) - view?.navigateToNext() - logRegisterEvent(studentsWithSemesters) - } - is Resource.Error -> { - view?.apply { - showProgress(false) - showContent(true) - } - lastError = it.error - loginErrorHandler.dispatch(it.error) - logRegisterEvent(studentsWithSemesters, it.error) - } + private fun registerStudents(studentsWithSemesters: List) { + flowWithResource { + val savedStudents = studentRepository.saveStudents(studentsWithSemesters) + val firstRegistered = studentsWithSemesters.first().apply { student.id = savedStudents.first() } + studentRepository.switchStudent(firstRegistered) + }.onEach { + when (it.status) { + Status.LOADING -> view?.run { + Timber.i("Registration started") + showProgress(true) + showContent(false) } - }.launch("register") + Status.SUCCESS -> { + Timber.i("Registration result: Success") + view?.openMainView() + logRegisterEvent(studentsWithSemesters) + } + Status.ERROR -> { + Timber.i("Registration result: An exception occurred ") + view?.apply { + showProgress(false) + showContent(true) + showContact(true) + } + lastError = it.error + loginErrorHandler.dispatch(it.error!!) + logRegisterEvent(studentsWithSemesters, it.error) + } + } + }.launch("register") } - private fun onEnterSymbol() { - view?.navigateToSymbol(loginData) - } - - private fun onDiscordClick() { + fun onDiscordClick() { view?.openDiscordInvite() } - private fun onEmailClick() { - view?.openEmail(lastError?.message.ifNullOrBlank { - loginData.baseUrl + "/" + loginData.symbol + "\n" + registerUser.symbols.filterNot { - (it.error is AccountPermissionException || it.error is InvalidSymbolException) && it.symbol != loginData.symbol - }.joinToString(";\n") { symbol -> - buildString { - append(" -") - append(symbol.symbol) - append("(${symbol.error?.message?.let { it.take(46) + "..." } ?: symbol.schools.size})") - if (symbol.schools.isNotEmpty()) { - append(": ") - } - append(symbol.schools.joinToString(", ") { unit -> - buildString { - append(unit.schoolShortName) - append("(${unit.error?.message?.let { it.take(46) + "..." } ?: unit.students.size})") - } - }) - } - } + "\nPozostałe: " + registerUser.symbols.filter { - it.error is AccountPermissionException || it.error is InvalidSymbolException - }.joinToString(", ") { it.symbol } - }) + fun onEmailClick() { + view?.openEmail(lastError?.message.ifNullOrBlank { "empty" }) } - private fun logRegisterEvent( - studentsWithSemesters: List, - error: Throwable? = null - ) { + private fun logRegisterEvent(studentsWithSemesters: List, error: Throwable? = null) { studentsWithSemesters.forEach { student -> analytics.logEvent( "registration_student_select", "success" to (error != null), "scrapperBaseUrl" to student.student.scrapperBaseUrl, "symbol" to student.student.symbol, - "error" to (error?.message?.ifBlank { "No message" } ?: "No error") - ) + "error" to (error?.message?.ifBlank { "No message" } ?: "No error")) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt index 39f312bf..f2acd76c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt @@ -1,19 +1,15 @@ package io.github.wulkanowy.ui.modules.login.studentselect +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView -import io.github.wulkanowy.ui.modules.login.LoginData interface LoginStudentSelectView : BaseView { - val symbols: Map - fun initView() - fun updateData(data: List) + fun updateData(data: List>) - fun navigateToSymbol(loginData: LoginData) - - fun navigateToNext() + fun openMainView() fun showProgress(show: Boolean) @@ -21,6 +17,8 @@ interface LoginStudentSelectView : BaseView { fun enableSignIn(enable: Boolean) + fun showContact(show: Boolean) + fun openDiscordInvite() fun openEmail(lastError: String) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt index 692aaeb7..e2c37db6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt @@ -7,18 +7,18 @@ import android.view.View.VISIBLE import android.view.inputmethod.EditorInfo.IME_ACTION_DONE import android.view.inputmethod.EditorInfo.IME_NULL import android.widget.ArrayAdapter -import androidx.core.os.bundleOf -import androidx.core.text.parseAsHtml import androidx.core.widget.doOnTextChanged import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.pojos.RegisterUser -import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.FragmentLoginSymbolBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.login.LoginData -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.openEmailClient +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.showSoftInput import javax.inject.Inject @AndroidEntryPoint @@ -31,36 +31,24 @@ class LoginSymbolFragment : @Inject lateinit var appInfo: AppInfo - @Inject - lateinit var preferencesRepository: PreferencesRepository - companion object { private const val SAVED_LOGIN_DATA = "LOGIN_DATA" - fun newInstance(loginData: LoginData) = LoginSymbolFragment().apply { - arguments = bundleOf(SAVED_LOGIN_DATA to loginData) - } + fun newInstance() = LoginSymbolFragment() } - override val symbolValue: String? get() = binding.loginSymbolName.text?.toString() - override val symbolNameError: CharSequence? get() = binding.loginSymbolNameLayout.error override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentLoginSymbolBinding.bind(view) - presenter.onAttachView( - view = this, - loginData = requireArguments().serializable(SAVED_LOGIN_DATA), - ) + presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_LOGIN_DATA)) } override fun initView() { - (requireActivity() as LoginActivity).showActionBar(true) - with(binding) { - loginSymbolSignIn.setOnClickListener { presenter.attemptLogin() } + loginSymbolSignIn.setOnClickListener { presenter.attemptLogin(loginSymbolName.text.toString()) } loginSymbolFaq.setOnClickListener { presenter.onFaqClick() } loginSymbolContactEmail.setOnClickListener { presenter.onEmailClick() } @@ -70,20 +58,13 @@ class LoginSymbolFragment : setOnEditorActionListener { _, id, _ -> if (id == IME_ACTION_DONE || id == IME_NULL) loginSymbolSignIn.callOnClick() else false } - setAdapter( - ArrayAdapter( - context, - android.R.layout.simple_list_item_1, - resources.getStringArray(R.array.symbols_values) - ) - ) + setAdapter(ArrayAdapter(context, android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values))) } } } - override fun setLoginToHeading(login: String) { - binding.loginSymbolHeader.text = - getString(R.string.login_header_symbol, login).parseAsHtml() + fun onParentInitSymbolFragment(loginData: Triple) { + presenter.onParentInitSymbolView(loginData) } override fun setErrorSymbolIncorrect() { @@ -93,21 +74,10 @@ class LoginSymbolFragment : } } - override fun setErrorSymbolInvalid() { - with(binding.loginSymbolNameLayout) { - requestFocus() - error = getString(R.string.login_invalid_symbol) - } - } - override fun setErrorSymbolRequire() { - setErrorSymbol(getString(R.string.error_field_required)) - } - - override fun setErrorSymbol(message: String) { - with(binding.loginSymbolNameLayout) { + binding.loginSymbolNameLayout.apply { requestFocus() - error = message + error = getString(R.string.login_field_required) } } @@ -138,8 +108,8 @@ class LoginSymbolFragment : binding.loginSymbolContainer.visibility = if (show) VISIBLE else GONE } - override fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) { - (activity as? LoginActivity)?.navigateToStudentSelect(loginData, registerUser) + override fun notifyParentAccountLogged(studentsWithSemesters: List) { + (activity as? LoginActivity)?.onSymbolFragmentAccountLogged(studentsWithSemesters) } override fun onSaveInstanceState(outState: Bundle) { @@ -157,10 +127,7 @@ class LoginSymbolFragment : } override fun openFaqPage() { - context?.openInternetBrowser( - "https://wulkanowy.github.io/czesto-zadawane-pytania/co-to-jest-symbol", - ::showMessage - ) + context?.openInternetBrowser("https://wulkanowy.github.io/czesto-zadawane-pytania/co-to-jest-symbol", ::showMessage) } override fun openEmail(host: String, lastError: String) { @@ -168,13 +135,11 @@ class LoginSymbolFragment : 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, + body = requireContext().getString(R.string.login_email_text, "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), - "${appInfo.versionName}-${appInfo.buildFlavor}", + appInfo.versionName, "$host/${binding.loginSymbolName.text}", - preferencesRepository.installationId, lastError ) ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt index 91fe1ac3..4593d880 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt @@ -1,20 +1,16 @@ package io.github.wulkanowy.ui.modules.login.symbol -import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.dataOrNull -import io.github.wulkanowy.data.onResourceNotLoading -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow -import io.github.wulkanowy.sdk.scrapper.getNormalizedSymbol -import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException 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.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank import kotlinx.coroutines.flow.onEach import timber.log.Timber +import java.io.Serializable import javax.inject.Inject class LoginSymbolPresenter @Inject constructor( @@ -25,22 +21,17 @@ class LoginSymbolPresenter @Inject constructor( private var lastError: Throwable? = null - lateinit var loginData: LoginData + var loginData: Triple? = null - private var registerUser: RegisterUser? = null - - fun onAttachView(view: LoginSymbolView, loginData: LoginData) { + @Suppress("UNCHECKED_CAST") + fun onAttachView(view: LoginSymbolView, savedLoginData: Serializable?) { super.onAttachView(view) - this.loginData = loginData - loginErrorHandler.onBadCredentials = { - view.setErrorSymbol(it.orEmpty()) - } - with(view) { + view.run { initView() showContact(false) - setLoginToHeading(loginData.login) - clearAndFocusSymbol() - showSoftKeyboard() + } + if (savedLoginData is Triple<*, *, *>) { + loginData = savedLoginData as Triple } } @@ -48,81 +39,58 @@ class LoginSymbolPresenter @Inject constructor( view?.apply { if (symbolNameError != null) clearSymbolError() } } - fun attemptLogin() { - if (view?.symbolValue.isNullOrBlank()) { + fun attemptLogin(symbol: String) { + if (loginData == null) throw IllegalArgumentException("Login data is null") + + if (symbol.isBlank()) { view?.setErrorSymbolRequire() return } - loginData = loginData.copy( - symbol = view?.symbolValue?.getNormalizedSymbol(), - ) - resourceFlow { - studentRepository.getUserSubjectsFromScrapper( - email = loginData.login, - password = loginData.password, - scrapperBaseUrl = loginData.baseUrl, - domainSuffix = loginData.domainSuffix, - symbol = loginData.symbol.orEmpty(), - ) - }.onEach { user -> - registerUser = user.dataOrNull - when (user) { - is Resource.Loading -> view?.run { + flowWithResource { studentRepository.getStudentsScrapper(loginData!!.first, loginData!!.second, loginData!!.third, symbol) }.onEach { + when (it.status) { + Status.LOADING -> view?.run { Timber.i("Login with symbol started") hideSoftKeyboard() showProgress(true) showContent(false) } - is Resource.Success -> { - when (user.data.symbols.size) { - 0 -> { + Status.SUCCESS -> { + view?.run { + if (it.data!!.isEmpty()) { Timber.i("Login with symbol result: Empty student list") - view?.run { - setErrorSymbolIncorrect() - showContact(true) - } - } - else -> { - val enteredSymbolDetails = user.data.symbols - .firstOrNull() - ?.takeIf { it.symbol == loginData.symbol } - - if (enteredSymbolDetails?.error is InvalidSymbolException) { - view?.run { - setErrorSymbolInvalid() - showContact(true) - } - } else { - Timber.i("Login with symbol result: Success") - view?.navigateToStudentSelect(loginData, requireNotNull(user.data)) - } + setErrorSymbolIncorrect() + view?.showContact(true) + } else { + Timber.i("Login with symbol result: Success") + notifyParentAccountLogged(it.data) } } analytics.logEvent( "registration_symbol", "success" to true, - "scrapperBaseUrl" to loginData.baseUrl, - "symbol" to view?.symbolValue, + "students" to it.data!!.size, + "scrapperBaseUrl" to loginData?.third, + "symbol" to symbol, "error" to "No error" ) } - is Resource.Error -> { + Status.ERROR -> { Timber.i("Login with symbol result: An exception occurred") analytics.logEvent( "registration_symbol", "success" to false, "students" to -1, - "scrapperBaseUrl" to loginData.baseUrl, - "symbol" to view?.symbolValue, - "error" to user.error.message.ifNullOrBlank { "No message" } + "scrapperBaseUrl" to loginData?.third, + "symbol" to symbol, + "error" to it.error!!.message.ifNullOrBlank { "No message" } ) - loginErrorHandler.dispatch(user.error) - lastError = user.error + loginErrorHandler.dispatch(it.error) + lastError = it.error view?.showContact(true) } } - }.onResourceNotLoading { + }.afterLoading { view?.apply { showProgress(false) showContent(true) @@ -130,17 +98,19 @@ class LoginSymbolPresenter @Inject constructor( }.launch("login") } + fun onParentInitSymbolView(loginData: Triple) { + this.loginData = loginData + view?.apply { + clearAndFocusSymbol() + showSoftKeyboard() + } + } + fun onFaqClick() { view?.openFaqPage() } fun onEmailClick() { - view?.openEmail(loginData.baseUrl, lastError?.message.ifNullOrBlank { - registerUser?.symbols?.flatMap { symbol -> - symbol.schools.map { it.error?.message } + symbol.error?.message - }?.filterNotNull()?.distinct()?.joinToString(";") { - it.take(46) + "..." - } ?: "blank" - }) + view?.openEmail(loginData?.third.orEmpty(), lastError?.message.ifNullOrBlank { "empty" }) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt index 6585c00f..830c77d1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt @@ -1,27 +1,18 @@ package io.github.wulkanowy.ui.modules.login.symbol -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView -import io.github.wulkanowy.ui.modules.login.LoginData interface LoginSymbolView : BaseView { - val symbolValue: String? - val symbolNameError: CharSequence? fun initView() - fun setLoginToHeading(login: String) - fun setErrorSymbolIncorrect() - fun setErrorSymbolInvalid() - fun setErrorSymbolRequire() - fun setErrorSymbol(message: String) - fun clearSymbolError() fun clearAndFocusSymbol() @@ -34,7 +25,7 @@ interface LoginSymbolView : BaseView { fun showContent(show: Boolean) - fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) + fun notifyParentAccountLogged(studentsWithSemesters: List) fun showContact(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt index a6c75c1d..0a73fe15 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt @@ -18,7 +18,7 @@ import javax.inject.Inject @AndroidEntryPoint class LuckyNumberFragment : BaseFragment(R.layout.fragment_lucky_number), LuckyNumberView, - MainView.TitledView, MainView.MainChildView { + MainView.TitledView { @Inject lateinit var presenter: LuckyNumberPresenter @@ -86,14 +86,6 @@ class LuckyNumberFragment : (activity as? MainActivity)?.pushView(LuckyNumberHistoryFragment.newInstance()) } - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onViewReselected() - } - - override fun popView() { - (activity as? MainActivity)?.popView() - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt index 2039624b..fd0598d8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt @@ -1,11 +1,14 @@ package io.github.wulkanowy.ui.modules.luckynumber -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.LuckyNumberRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -31,45 +34,47 @@ class LuckyNumberPresenter @Inject constructor( } private fun loadData(forceRefresh: Boolean = false) { - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() luckyNumberRepository.getLuckyNumber(student, forceRefresh) - } - .logResourceStatus("load lucky number") - .onResourceData { - if (it != null) { - view?.apply { - updateData(it) - showContent(true) - showEmpty(false) - showErrorView(false) - } - } else { - view?.run { - showContent(false) - showEmpty(true) - showErrorView(false) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading lucky number started") + Status.SUCCESS -> { + if (it.data != null) { + Timber.i("Loading lucky number result: Success") + view?.apply { + updateData(it.data) + showContent(true) + showEmpty(false) + showErrorView(false) + } + analytics.logEvent( + "load_item", + "type" to "lucky_number", + "number" to it.data.luckyNumber + ) + } else { + Timber.i("Loading lucky number result: No lucky number found") + view?.run { + showContent(false) + showEmpty(true) + showErrorView(false) + } } } - } - .onResourceSuccess { - if (it != null) { - analytics.logEvent( - "load_item", - "type" to "lucky_number", - "number" to it.luckyNumber - ) + Status.ERROR -> { + Timber.i("Loading lucky number result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceNotLoading { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } + }.afterLoading { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -99,9 +104,4 @@ class LuckyNumberPresenter @Inject constructor( fun onDetailsClick() { view?.showErrorDetailsDialog(lastError) } - - fun onViewReselected() { - Timber.i("Luckynumber view is reselected") - view?.popView() - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt index 3d34ebc8..0c05a156 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt @@ -26,6 +26,4 @@ interface LuckyNumberView : BaseView { fun showContent(show: Boolean) fun openLuckyNumberHistory() - - fun popView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt index a78ce5dd..dc141f8d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt @@ -5,6 +5,8 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.datepicker.CalendarConstraints +import com.google.android.material.datepicker.MaterialDatePicker import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.LuckyNumber @@ -12,9 +14,11 @@ import io.github.wulkanowy.databinding.FragmentLuckyNumberHistoryBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.SchoolDaysValidator import io.github.wulkanowy.utils.dpToPx -import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear -import io.github.wulkanowy.utils.openMaterialDatePicker +import io.github.wulkanowy.utils.schoolYearStart +import io.github.wulkanowy.utils.toLocalDateTime +import io.github.wulkanowy.utils.toTimestamp import java.time.LocalDate import javax.inject.Inject @@ -61,7 +65,7 @@ class LuckyNumberHistoryFragment : luckyNumberHistoryPreviousButton.setOnClickListener { presenter.onPreviousWeek() } luckyNumberHistoryNextButton.setOnClickListener { presenter.onNextWeek() } - luckyNumberHistoryNavContainer.elevation = requireContext().dpToPx(3f) + luckyNumberHistoryNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -107,15 +111,27 @@ class LuckyNumberHistoryFragment : binding.luckyNumberHistoryNextButton.visibility = if (show) VISIBLE else View.INVISIBLE } - override fun showDatePickerDialog(selectedDate: LocalDate) { - openMaterialDatePicker( - selected = selectedDate, - rangeStart = selectedDate.firstSchoolDayInSchoolYear, - rangeEnd = LocalDate.now().plusWeeks(1), - onDateSelected = { - presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth) - } - ) + override fun showDatePickerDialog(currentDate: LocalDate) { + val baseDate = currentDate.schoolYearStart + val rangeStart = baseDate.toTimestamp() + val rangeEnd = LocalDate.now().plusWeeks(1).toTimestamp() + + val constraintsBuilder = CalendarConstraints.Builder().apply { + setValidator(SchoolDaysValidator(rangeStart, rangeEnd)) + setStart(rangeStart) + setEnd(rangeEnd) + } + val datePicker = MaterialDatePicker.Builder.datePicker() + .setCalendarConstraints(constraintsBuilder.build()) + .setSelection(currentDate.toTimestamp()) + .build() + + datePicker.addOnPositiveButtonClickListener { + val date = it.toLocalDateTime() + presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth) + } + + datePicker.show(this@LuckyNumberHistoryFragment.parentFragmentManager, null) } override fun showContent(show: Boolean) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt index fc753950..c45cb69a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt @@ -1,12 +1,24 @@ package io.github.wulkanowy.ui.modules.luckynumber.history +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.LuckyNumberRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.* -import kotlinx.coroutines.flow.* +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource +import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.previousOrSameSchoolDay +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach import timber.log.Timber import java.time.LocalDate import javax.inject.Inject @@ -40,51 +52,55 @@ class LuckyNumberHistoryPresenter @Inject constructor( flow { val student = studentRepository.getCurrentStudent() emit(semesterRepository.getCurrentSemester(student)) - } - .catch { Timber.i("Loading semester result: An exception occurred") } - .onEach { - currentDate = currentDate.getLastSchoolDayIfHoliday(it.schoolYear) - reloadNavigation() - } - .launch("holidays") + }.catch { + Timber.i("Loading semester result: An exception occurred") + }.onEach { + currentDate = currentDate.getLastSchoolDayIfHoliday(it.schoolYear) + reloadNavigation() + }.launch("holidays") } private fun loadData() { - flow { + flowWithResource { val student = studentRepository.getCurrentStudent() - emitAll( - luckyNumberRepository.getLuckyNumberHistory( - student = student, - start = currentDate.monday, - end = currentDate.sunday - ) - ) - } - .onEach { - if (!it.isNullOrEmpty()) { - view?.apply { - updateData(it) - showContent(true) - showEmpty(false) - showErrorView(false) - showProgress(false) - } - } else { - view?.run { - showContent(false) - showEmpty(true) - showErrorView(false) - showProgress(false) + luckyNumberRepository.getLuckyNumberHistory(student, currentDate.monday, currentDate.sunday) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading lucky number history started") + Status.SUCCESS -> { + if (!it.data?.first().isNullOrEmpty()) { + Timber.i("Loading lucky number result: Success") + view?.apply { + updateData(it.data!!.first()) + showContent(true) + showEmpty(false) + showErrorView(false) + showProgress(false) + } + analytics.logEvent( + "load_items", + "type" to "lucky_number_history", + "numbers" to it.data + ) + } else { + Timber.i("Loading lucky number history result: No lucky numbers found") + view?.run { + showContent(false) + showEmpty(true) + showErrorView(false) + } } } - - analytics.logEvent( - "load_items", - "type" to "lucky_number_history", - ) + Status.ERROR -> { + Timber.i("Loading lucky number history result: An exception occurred") + errorHandler.dispatch(it.error!!) + } } - .catch { errorHandler.dispatch(it) } - .launchIn(presenterScope) + }.afterLoading { + view?.run { + showProgress(false) + } + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -127,10 +143,8 @@ class LuckyNumberHistoryPresenter @Inject constructor( view?.apply { showPreButton(!currentDate.minusDays(7).isHolidays) showNextButton(!currentDate.plusDays(7).isHolidays) - updateNavigationWeek( - "${currentDate.monday.toFormattedString("dd.MM")} - " + - currentDate.sunday.toFormattedString("dd.MM") - ) + updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " + + currentDate.sunday.toFormattedString("dd.MM")) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt index 7b9b0294..331e4ff8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt @@ -28,7 +28,7 @@ interface LuckyNumberHistoryView : BaseView { fun showNextButton(show: Boolean) - fun showDatePickerDialog(selectedDate: LocalDate) + fun showDatePickerDialog(currentDate: LocalDate) fun showContent(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt index a2d23e54..024beff8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt @@ -1,12 +1,16 @@ package io.github.wulkanowy.ui.modules.luckynumberwidget -import android.appwidget.AppWidgetManager.* +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS import android.content.Intent +import android.os.Build import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.ActivityWidgetConfigureBinding import io.github.wulkanowy.ui.base.BaseActivity @@ -37,6 +41,7 @@ class LuckyNumberWidgetConfigureActivity : setContentView( ActivityWidgetConfigureBinding.inflate(layoutInflater).apply { binding = this }.root ) + intent.extras.let { presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID)) } @@ -51,6 +56,22 @@ class LuckyNumberWidgetConfigureActivity : configureAdapter.onClickListener = presenter::onItemSelect } + override fun showThemeDialog() { + var items = arrayOf( + getString(R.string.widget_timetable_theme_light), + getString(R.string.widget_timetable_theme_dark) + ) + if (appInfo.systemVersion >= Build.VERSION_CODES.Q) items += (getString(R.string.widget_timetable_theme_system)) + + dialog = AlertDialog.Builder(this, R.style.WulkanowyTheme_WidgetAccountSwitcher) + .setTitle(R.string.widget_timetable_theme_title) + .setOnDismissListener { presenter.onDismissThemeView() } + .setSingleChoiceItems(items, -1) { _, which -> + presenter.onThemeSelect(which) + } + .show() + } + override fun updateData(data: List, selectedStudentId: Long) { with(configureAdapter) { selectedId = selectedStudentId diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt index 7e53dad0..5b6af69a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt @@ -1,13 +1,14 @@ package io.github.wulkanowy.ui.modules.luckynumberwidget -import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getThemeWidgetKey +import io.github.wulkanowy.utils.flowWithResource import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -31,24 +32,39 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor( fun onItemSelect(student: Student) { selectedStudent = student + view?.showThemeDialog() + } + + fun onThemeSelect(index: Int) { + appWidgetId?.let { + sharedPref.putLong(getThemeWidgetKey(it), index.toLong()) + } registerStudent(selectedStudent) } + fun onDismissThemeView() { + view?.finishView() + } + private fun loadData() { - resourceFlow { studentRepository.getSavedStudents(false) }.onEach { - when (it) { - is Resource.Loading -> Timber.d("Lucky number widget configure students data load") - is Resource.Success -> { + flowWithResource { studentRepository.getSavedStudents(false) }.onEach { + when (it.status) { + Status.LOADING -> Timber.d("Lucky number widget configure students data load") + Status.SUCCESS -> { val selectedStudentId = appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } ?: -1 + when { - it.data.isEmpty() -> view?.openLoginView() - it.data.size == 1 -> onItemSelect(it.data.single().student) + it.data!!.isEmpty() -> view?.openLoginView() + it.data.size == 1 -> { + selectedStudent = it.data.single().student + view?.showThemeDialog() + } else -> view?.updateData(it.data, selectedStudentId) } } - is Resource.Error -> errorHandler.dispatch(it.error) + Status.ERROR -> errorHandler.dispatch(it.error!!) } }.launch() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt index df13b993..b4556f7e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt @@ -7,6 +7,8 @@ interface LuckyNumberWidgetConfigureView : BaseView { fun initView() + fun showThemeDialog() + fun updateData(data: List, selectedStudentId: Long) fun updateLuckyNumberWidget(widgetId: Int) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt index bafb2d7e..49a19943 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt @@ -1,26 +1,26 @@ package io.github.wulkanowy.ui.modules.luckynumberwidget import android.app.PendingIntent +import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT +import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH import android.appwidget.AppWidgetProvider import android.content.Context import android.content.res.Configuration import android.os.Bundle -import android.util.TypedValue.COMPLEX_UNIT_SP -import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE import android.widget.RemoteViews import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.dataOrNull import io.github.wulkanowy.data.db.SharedPrefProvider -import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.data.repositories.LuckyNumberRepository import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.toFirstResult -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.utils.PendingIntentCompat +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.toFirstResult import kotlinx.coroutines.runBlocking import timber.log.Timber import javax.inject.Inject @@ -38,100 +38,93 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { lateinit var sharedPref: SharedPrefProvider companion object { - private const val LUCKY_NUMBER_WIDGET_MAX_SIZE = 196 - - private const val LUCKY_NUMBER_PENDING_INTENT_ID = 300 - private const val LUCKY_NUMBER_HISTORY_PENDING_INTENT_ID = 301 fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$appWidgetId" + + fun getThemeWidgetKey(appWidgetId: Int) = "lucky_number_widget_theme_$appWidgetId" + + fun getHeightWidgetKey(appWidgetId: Int) = "lucky_number_widget_height_$appWidgetId" + + fun getWidthWidgetKey(appWidgetId: Int) = "lucky_number_widget_width_$appWidgetId" } - override fun onUpdate( - context: Context, - appWidgetManager: AppWidgetManager, - appWidgetIds: IntArray? - ) { + override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray?) { super.onUpdate(context, appWidgetManager, appWidgetIds) + appWidgetIds?.forEach { appWidgetId -> - val appIntent = PendingIntent.getActivity( - context, - LUCKY_NUMBER_PENDING_INTENT_ID, - SplashActivity.getStartIntent(context, Destination.LuckyNumber), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) + val luckyNumber = getLuckyNumber(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId) + val appIntent = PendingIntent.getActivity(context, MainView.Section.LUCKY_NUMBER.id, + MainActivity.getStartIntent(context, MainView.Section.LUCKY_NUMBER, true), FLAG_UPDATE_CURRENT) - val historyIntent = PendingIntent.getActivity( - context, - LUCKY_NUMBER_HISTORY_PENDING_INTENT_ID, - SplashActivity.getStartIntent(context, Destination.LuckyNumberHistory), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) + val remoteView = RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context)).apply { + setTextViewText(R.id.luckyNumberWidgetNumber, luckyNumber?.luckyNumber?.toString() ?: "#") + setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent) + } - appWidgetIds?.forEach { widgetId -> - val studentId = sharedPref.getLong(getStudentWidgetKey(widgetId), 0) - val luckyNumberResource = getLuckyNumber(studentId, widgetId) - val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber?.toString() - val remoteView = RemoteViews(context.packageName, R.layout.widget_luckynumber) - .apply { - setTextViewText(R.id.luckyNumberWidgetValue, luckyNumber ?: "-") - setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent) - setOnClickPendingIntent(R.id.luckyNumberWidgetHistoryButton, historyIntent) - } - - resizeWidget(context, appWidgetManager.getAppWidgetOptions(widgetId), remoteView) - appWidgetManager.updateAppWidget(widgetId, remoteView) - } - } - - override fun onAppWidgetOptionsChanged( - context: Context?, - appWidgetManager: AppWidgetManager?, - appWidgetId: Int, - newOptions: Bundle? - ) { - super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions) - - if (context == null || newOptions == null || appWidgetManager == null) { - return - } - - val remoteView = RemoteViews(context.packageName, R.layout.widget_luckynumber) - resizeWidget(context, newOptions, remoteView) - appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteView) - } - - private fun resizeWidget(context: Context, options: Bundle, remoteViews: RemoteViews) { - val (width, height) = options.getWidgetSize(context) - val size = minOf(width, height, LUCKY_NUMBER_WIDGET_MAX_SIZE).toFloat() - resizeWidgetContents(size, remoteViews) - Timber.v("LuckyNumberWidget resized: ${width}x${height} ($size)") - } - - private fun resizeWidgetContents(size: Float, remoteViews: RemoteViews) { - var historyButtonVisibility = View.VISIBLE - var luckyNumberTextSize = 72f - - if (size < 150) { - luckyNumberTextSize = 44f - historyButtonVisibility = View.GONE - } - if (size < 75) { - luckyNumberTextSize = 26f - } - - remoteViews.apply { - setTextViewTextSize(R.id.luckyNumberWidgetValue, COMPLEX_UNIT_SP, luckyNumberTextSize) - setViewVisibility(R.id.luckyNumberWidgetHistoryButton, historyButtonVisibility) + setStyles(remoteView, appWidgetId) + appWidgetManager.updateAppWidget(appWidgetId, remoteView) } } override fun onDeleted(context: Context?, appWidgetIds: IntArray?) { super.onDeleted(context, appWidgetIds) appWidgetIds?.forEach { appWidgetId -> - sharedPref.delete(getStudentWidgetKey(appWidgetId)) + with(sharedPref) { + delete(getHeightWidgetKey(appWidgetId)) + delete(getStudentWidgetKey(appWidgetId)) + delete(getThemeWidgetKey(appWidgetId)) + delete(getWidthWidgetKey(appWidgetId)) + } } } + override fun onAppWidgetOptionsChanged(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, newOptions: Bundle?) { + super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions) + + val remoteView = RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context)) + + setStyles(remoteView, appWidgetId, newOptions) + appWidgetManager.updateAppWidget(appWidgetId, remoteView) + } + + private fun setStyles(views: RemoteViews, appWidgetId: Int, options: Bundle? = null) { + val width = options?.getInt(OPTION_APPWIDGET_MIN_WIDTH) ?: sharedPref.getLong(getWidthWidgetKey(appWidgetId), 74).toInt() + val height = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: sharedPref.getLong(getHeightWidgetKey(appWidgetId), 74).toInt() + + with(sharedPref) { + putLong(getWidthWidgetKey(appWidgetId), width.toLong()) + putLong(getHeightWidgetKey(appWidgetId), height.toLong()) + } + + val rows = getCellsForSize(height) + val cols = getCellsForSize(width) + + Timber.d("New lucky number widget measurement: %dx%d", width, height) + Timber.d("Widget size: $cols x $rows") + + when { + 1 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = false) + 1 == cols && 1 < rows -> views.setVisibility(imageTop = true, imageLeft = false) + 1 < cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true) + 1 == cols && 1 == rows -> views.setVisibility(imageTop = true, imageLeft = false) + 2 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true) + else -> views.setVisibility(imageTop = false, imageLeft = false, title = true) + } + } + + private fun RemoteViews.setVisibility(imageTop: Boolean, imageLeft: Boolean, title: Boolean = false) { + setViewVisibility(R.id.luckyNumberWidgetImageTop, if (imageTop) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetImageLeft, if (imageLeft) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetTitle, if (title) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) + } + + private fun getCellsForSize(size: Int): Int { + var n = 2 + while (74 * n - 30 < size) ++n + return n - 1 + } + private fun getLuckyNumber(studentId: Long, appWidgetId: Int) = runBlocking { try { val students = studentRepository.getSavedStudents() @@ -146,31 +139,25 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { else -> null } - if (currentStudent != null) { - luckyNumberRepository.getLuckyNumber(currentStudent, forceRefresh = false) - .toFirstResult() - } else { - Resource.Success(null) + currentStudent?.let { + luckyNumberRepository.getLuckyNumber(it, false).toFirstResult().data } } catch (e: Exception) { - Timber.e(e, "An error has occurred in lucky number provider") - Resource.Error(e) + if (e.cause !is NoCurrentStudentException) { + Timber.e(e, "An error has occurred in lucky number provider") + } + null } } - private fun Bundle.getWidgetSize(context: Context): Pair { - val minWidth = getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH) - val maxWidth = getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH) - val minHeight = getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT) - val maxHeight = getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT) + private fun getCorrectLayoutId(appWidgetId: Int, context: Context): Int { + val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0) + val isSystemDarkMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES - val orientation = context.resources.configuration.orientation - val isPortrait = orientation == Configuration.ORIENTATION_PORTRAIT - - return if (isPortrait) { - minWidth to maxHeight + return if (savedTheme == 1L || (savedTheme == 2L && isSystemDarkMode)) { + R.layout.widget_luckynumber_dark } else { - maxWidth to minHeight + R.layout.widget_luckynumber } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index 091080a5..d758ac0d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -1,20 +1,26 @@ package io.github.wulkanowy.ui.modules.main +import android.annotation.SuppressLint import android.content.Context import android.content.Intent +import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.content.pm.ShortcutInfo +import android.content.pm.ShortcutManager +import android.graphics.drawable.Icon import android.os.Build +import android.os.Build.VERSION_CODES.P import android.os.Bundle import android.view.Menu import android.view.MenuItem -import android.view.ViewGroup.MarginLayoutParams -import androidx.activity.OnBackPressedCallback -import androidx.activity.addCallback -import androidx.core.view.* +import androidx.annotation.RequiresApi +import androidx.core.content.getSystemService +import androidx.core.view.ViewCompat +import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.elevation.ElevationOverlayProvider import com.ncapdevi.fragnav.FragNavController import com.ncapdevi.fragnav.FragNavController.Companion.HIDE @@ -23,14 +29,30 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.ActivityMainBinding -import io.github.wulkanowy.databinding.DialogAdsConsentBinding import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog -import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem -import io.github.wulkanowy.utils.* -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json +import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment +import io.github.wulkanowy.ui.modules.conference.ConferenceFragment +import io.github.wulkanowy.ui.modules.dashboard.DashboardFragment +import io.github.wulkanowy.ui.modules.exam.ExamFragment +import io.github.wulkanowy.ui.modules.grade.GradeFragment +import io.github.wulkanowy.ui.modules.homework.HomeworkFragment +import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment +import io.github.wulkanowy.ui.modules.message.MessageFragment +import io.github.wulkanowy.ui.modules.more.MoreFragment +import io.github.wulkanowy.ui.modules.note.NoteFragment +import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment +import io.github.wulkanowy.ui.modules.timetable.TimetableFragment +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.InAppReviewHelper +import io.github.wulkanowy.utils.UpdateHelper +import io.github.wulkanowy.utils.createNameInitialsDrawable +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.nickOrName +import io.github.wulkanowy.utils.safelyPopFragments +import io.github.wulkanowy.utils.setOnViewChangeListener import timber.log.Timber import javax.inject.Inject @@ -53,8 +75,6 @@ class MainActivity : BaseActivity(), MainVie @Inject lateinit var appInfo: AppInfo - private var onBackCallback: OnBackPressedCallback? = null - private var accountMenu: MenuItem? = null private val overlayProvider by lazy { ElevationOverlayProvider(this) } @@ -63,14 +83,15 @@ class MainActivity : BaseActivity(), MainVie FragNavController(supportFragmentManager, R.id.main_fragment_container) companion object { - - private const val EXTRA_START_DESTINATION = "start_destination_json" + const val EXTRA_START_MENU = "extraStartMenu" fun getStartIntent( context: Context, - destination: Destination? = null, + startMenu: MainView.Section? = null, + clear: Boolean = false ) = Intent(context, MainActivity::class.java).apply { - destination?.let { putExtra(EXTRA_START_DESTINATION, Json.encodeToString(it)) } + if (clear) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK + startMenu?.let { putExtra(EXTRA_START_MENU, it.id) } } } @@ -79,36 +100,48 @@ class MainActivity : BaseActivity(), MainVie override val currentStackSize get() = navController.currentStack?.size override val currentViewTitle - get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId - ?.let { getString(it) } + get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let { + getString(it) + } override val currentViewSubtitle get() = (navController.currentFrag as? MainView.TitledView)?.subtitleString - private var savedInstanceState: Bundle? = null + override var startMenuIndex = 0 + override var startMenuMoreIndex = -1 + + private val moreMenuFragments = mapOf( + MainView.Section.MESSAGE.id to MessageFragment.newInstance(), + MainView.Section.EXAM.id to ExamFragment.newInstance(), + MainView.Section.HOMEWORK.id to HomeworkFragment.newInstance(), + MainView.Section.NOTE.id to NoteFragment.newInstance(), + MainView.Section.CONFERENCE.id to ConferenceFragment.newInstance(), + MainView.Section.SCHOOL_ANNOUNCEMENT.id to SchoolAnnouncementFragment.newInstance(), + MainView.Section.LUCKY_NUMBER.id to LuckyNumberFragment.newInstance(), + ) + + @SuppressLint("NewApi") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(ActivityMainBinding.inflate(layoutInflater).apply { binding = this }.root) setSupportActionBar(binding.mainToolbar) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - WindowCompat.setDecorFitsSystemWindows(window, false) - binding.mainAppBar.isLifted = true - } - initializeFragmentContainer() - - this.savedInstanceState = savedInstanceState messageContainer = binding.mainMessageContainer - messageAnchor = binding.mainMessageContainer updateHelper.messageContainer = binding.mainFragmentContainer - onBackCallback = onBackPressedDispatcher.addCallback(this, enabled = false) { - presenter.onBackPressed() + + val section = MainView.Section.values() + .singleOrNull { it.id == intent.getIntExtra(EXTRA_START_MENU, -1) } + + presenter.onAttachView(this, section) + + with(navController) { + initialize(startMenuIndex, savedInstanceState) + pushFragment(moreMenuFragments[startMenuMoreIndex]) } - val destination = intent.getStringExtra(EXTRA_START_DESTINATION) - ?.takeIf { savedInstanceState == null } + if (appInfo.systemVersion >= Build.VERSION_CODES.N_MR1) { + initShortcuts() + } - presenter.onAttachView(this, destination) updateHelper.checkAndInstallUpdates(this) } @@ -118,91 +151,123 @@ class MainActivity : BaseActivity(), MainVie } //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) } - override fun onCreateOptionsMenu(menu: Menu): Boolean { + @RequiresApi(Build.VERSION_CODES.N_MR1) + fun initShortcuts() { + val shortcutsList = mutableListOf() + + listOf( + Triple( + getString(R.string.grade_title), + R.drawable.ic_shortcut_grade, + MainView.Section.GRADE + ), + Triple( + getString(R.string.attendance_title), + R.drawable.ic_shortcut_attendance, + MainView.Section.ATTENDANCE + ), + Triple( + getString(R.string.exam_title), + R.drawable.ic_shortcut_exam, + MainView.Section.EXAM + ), + Triple( + getString(R.string.timetable_title), + R.drawable.ic_shortcut_timetable, + MainView.Section.TIMETABLE + ) + ).forEach { (title, icon, enum) -> + shortcutsList.add( + ShortcutInfo.Builder(applicationContext, title) + .setShortLabel(title) + .setLongLabel(title) + .setIcon(Icon.createWithResource(applicationContext, icon)) + .setIntents( + arrayOf( + Intent(applicationContext, MainActivity::class.java) + .setAction(Intent.ACTION_VIEW), + Intent(applicationContext, MainActivity::class.java) + .putExtra(EXTRA_START_MENU, enum.id) + .setAction(Intent.ACTION_VIEW) + .addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK) + ) + ) + .build() + ) + } + + getSystemService()?.dynamicShortcuts = shortcutsList + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.action_menu_main, menu) - accountMenu = menu.findItem(R.id.mainMenuAccount) + accountMenu = menu?.findItem(R.id.mainMenuAccount) presenter.onActionMenuCreated() return true } - override fun initView( - startMenuIndex: Int, - rootAppMenuItems: List, - rootUpdatedDestinations: List - ) { - initializeToolbar() - initializeBottomNavigation(startMenuIndex, rootAppMenuItems) - initializeNavController(startMenuIndex, rootUpdatedDestinations) - } - - private fun initializeNavController( - startMenuIndex: Int, - rootUpdatedDestinations: List - ) { - with(navController) { - setOnViewChangeListener { destinationView -> - presenter.onViewChange(destinationView) - analytics.setCurrentScreen( - this@MainActivity, - destinationView::class.java.simpleName - ) - } - fragmentHideStrategy = HIDE - rootFragments = rootUpdatedDestinations.map { it.destinationFragment } - - initialize(startMenuIndex, savedInstanceState) - } - savedInstanceState = null - } - - private fun initializeToolbar() { + @SuppressLint("NewApi") + override fun initView() { with(binding.mainToolbar) { stateListAnimator = null setBackgroundColor( overlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(4f)) ) } - } - private fun initializeBottomNavigation( - startMenuIndex: Int, - rootAppMenuItems: List - ) { with(binding.mainBottomNav) { with(menu) { - rootAppMenuItems.forEachIndexed { index, item -> - add(Menu.NONE, index, Menu.NONE, item.title) - .setIcon(item.icon) - } + add(Menu.NONE, 0, Menu.NONE, R.string.dashboard_title) + .setIcon(R.drawable.ic_main_dashboard) + add(Menu.NONE, 1, Menu.NONE, R.string.grade_title) + .setIcon(R.drawable.ic_main_grade) + add(Menu.NONE, 2, Menu.NONE, R.string.attendance_title) + .setIcon(R.drawable.ic_main_attendance) + add(Menu.NONE, 3, Menu.NONE, R.string.timetable_title) + .setIcon(R.drawable.ic_main_timetable) add(Menu.NONE, 4, Menu.NONE, R.string.more_title) .setIcon(R.drawable.ic_main_more) } selectedItemId = startMenuIndex - setOnItemSelectedListener { - this@MainActivity.presenter.onTabSelected(it.itemId, false) - } - setOnItemReselectedListener { - this@MainActivity.presenter.onTabSelected(it.itemId, true) - } + setOnItemSelectedListener { presenter.onTabSelected(it.itemId, false) } + setOnItemReselectedListener { presenter.onTabSelected(it.itemId, true) } } - } - private fun initializeFragmentContainer() { - ViewCompat.setOnApplyWindowInsetsListener(binding.mainFragmentContainer) { view, insets -> - val bottomInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) + with(navController) { + setOnViewChangeListener { section, name -> + if (section == MainView.Section.ACCOUNT || section == MainView.Section.STUDENT_INFO) { + binding.mainBottomNav.isVisible = false - view.updateLayoutParams { - bottomMargin = if (binding.mainBottomNav.isVisible) 0 else bottomInsets.bottom + if (appInfo.systemVersion >= P) { + window.navigationBarColor = getThemeAttrColor(R.attr.colorSurface) + } + } else { + binding.mainBottomNav.isVisible = true + + if (appInfo.systemVersion >= P) { + window.navigationBarColor = + getThemeAttrColor(android.R.attr.navigationBarColor) + } + } + + analytics.setCurrentScreen(this@MainActivity, name) + presenter.onViewChange(section) } - WindowInsetsCompat.CONSUMED + fragmentHideStrategy = HIDE + rootFragments = listOf( + DashboardFragment.newInstance(), + GradeFragment.newInstance(), + AttendanceFragment.newInstance(), + TimetableFragment.newInstance(), + MoreFragment.newInstance() + ) } } @@ -210,10 +275,8 @@ class MainActivity : BaseActivity(), MainVie caller: PreferenceFragmentCompat, pref: Preference ): Boolean { - val fragment = supportFragmentManager.fragmentFactory.instantiate( - classLoader, - pref.fragment.toString() - ) + val fragment = + supportFragmentManager.fragmentFactory.instantiate(classLoader, pref.fragment) pushView(fragment) return true } @@ -250,13 +313,8 @@ class MainActivity : BaseActivity(), MainVie showDialogFragment(AccountQuickDialog.newInstance(studentWithSemesters)) } - override fun showBottomNavigation(show: Boolean) { - binding.mainBottomNav.isVisible = show - binding.mainFragmentContainer.requestApplyInsets() - } - - override fun openMoreDestination(destination: Destination) { - pushView(destination.destinationFragment) + override fun showActionBarElevation(show: Boolean) { + ViewCompat.setElevation(binding.mainToolbar, if (show) dpToPx(4f) else 0f) } override fun notifyMenuViewReselected() { @@ -288,7 +346,6 @@ class MainActivity : BaseActivity(), MainVie analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName) navController.pushFragment(fragment) - onBackCallback?.isEnabled = !isRootView } override fun popView(depth: Int) { @@ -296,7 +353,10 @@ class MainActivity : BaseActivity(), MainVie analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName) navController.safelyPopFragments(depth) - onBackCallback?.isEnabled = !isRootView + } + + override fun onBackPressed() { + presenter.onBackPressed { super.onBackPressed() } } override fun showStudentAvatar(student: Student) { @@ -310,52 +370,9 @@ class MainActivity : BaseActivity(), MainVie inAppReviewHelper.showInAppReview(this) } - override fun showAppSupport() { - MaterialAlertDialogBuilder(this) - .setTitle(R.string.main_support_title) - .setMessage(R.string.main_support_description) - .setPositiveButton(R.string.main_support_positive) { _, _ -> presenter.onEnableAdsSelected() } - .setNegativeButton(android.R.string.cancel) { _, _ -> } - .setOnDismissListener { } - .show() - } - - override fun showPrivacyPolicyDialog() { - val dialogAdsConsentBinding = DialogAdsConsentBinding.inflate(layoutInflater) - - val dialog = MaterialAlertDialogBuilder(this) - .setTitle(R.string.pref_ads_consent_title) - .setMessage(R.string.pref_ads_consent_description) - .setView(dialogAdsConsentBinding.root) - .show() - - dialogAdsConsentBinding.adsConsentOver.setOnCheckedChangeListener { _, isChecked -> - dialogAdsConsentBinding.adsConsentPersonalised.isEnabled = isChecked - } - - dialogAdsConsentBinding.adsConsentPersonalised.setOnClickListener { - presenter.onPrivacyAgree(true) - dialog.dismiss() - } - - dialogAdsConsentBinding.adsConsentNonPersonalised.setOnClickListener { - presenter.onPrivacyAgree(false) - dialog.dismiss() - } - - dialogAdsConsentBinding.adsConsentPrivacy.setOnClickListener { presenter.onPrivacySelected() } - dialogAdsConsentBinding.adsConsentCancel.setOnClickListener { dialog.cancel() } - } - - override fun openPrivacyPolicy() { - openInternetBrowser( - "https://wulkanowy.github.io/polityka-prywatnosci.html", - ::showMessage - ) - } - override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) navController.onSaveInstanceState(outState) + intent.removeExtra(EXTRA_START_MENU) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt index ae05ecf2..4805b5a1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -1,81 +1,45 @@ package io.github.wulkanowy.ui.modules.main +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.onResourceError -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.services.sync.SyncManager import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.BaseView import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.account.AccountView -import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsView -import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView -import io.github.wulkanowy.utils.AdsHelper +import io.github.wulkanowy.ui.modules.main.MainView.Section.GRADE +import io.github.wulkanowy.ui.modules.main.MainView.Section.MESSAGE +import io.github.wulkanowy.ui.modules.main.MainView.Section.SCHOOL import io.github.wulkanowy.utils.AnalyticsHelper -import io.github.wulkanowy.utils.AppInfo -import kotlinx.coroutines.launch -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json +import io.github.wulkanowy.utils.flowWithResource +import kotlinx.coroutines.flow.onEach import timber.log.Timber -import java.time.Duration -import java.time.Instant +import java.time.LocalDate import javax.inject.Inject class MainPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, - private val preferencesRepository: PreferencesRepository, + private val prefRepository: PreferencesRepository, private val syncManager: SyncManager, private val analytics: AnalyticsHelper, - private val json: Json, - private val adsHelper: AdsHelper, - private val appInfo: AppInfo ) : BasePresenter(errorHandler, studentRepository) { private var studentsWitSemesters: List? = null - private val rootAppMenuItems = preferencesRepository.appMenuItemOrder - .sortedBy { it.order } - .take(4) - - private val rootDestinationTypeList = rootAppMenuItems.map { it.destinationType } - .plus(Destination.Type.MORE) - - private val Destination?.startMenuIndex - get() = when { - this == null -> 0 - destinationType in rootDestinationTypeList -> { - rootDestinationTypeList.indexOf(destinationType) - } - else -> 4 - } - - fun onAttachView(view: MainView, initDestinationJson: String?) { + fun onAttachView(view: MainView, initMenu: MainView.Section?) { super.onAttachView(view) - - val initDestination: Destination? = initDestinationJson?.let { json.decodeFromString(it) } - - val startMenuIndex = initDestination.startMenuIndex - val destinations = rootDestinationTypeList.map { - if (it == initDestination?.destinationType) initDestination else it.defaultDestination - } - - view.initView(startMenuIndex, rootAppMenuItems, destinations) - if (initDestination != null && startMenuIndex == 4) { - view.openMoreDestination(initDestination) + view.apply { + getProperViewIndexes(initMenu).let { (main, more) -> + startMenuIndex = main + startMenuMoreIndex = more + } + initView() + Timber.i("Main view was initialized with $startMenuIndex menu index and $startMenuMoreIndex more index") } syncManager.startPeriodicSyncWorker() - - checkAppSupport() - - analytics.logEvent("app_open", "destination" to initDestination.toString()) - Timber.i("Main view was initialized with $initDestination") + analytics.logEvent("app_open", "destination" to initMenu?.name) } fun onActionMenuCreated() { @@ -84,19 +48,25 @@ class MainPresenter @Inject constructor( return } - resourceFlow { studentRepository.getSavedStudents(false) } - .logResourceStatus("load student avatar") - .onResourceSuccess { - studentsWitSemesters = it - showCurrentStudentAvatar() - } - .onResourceError(errorHandler::dispatch) - .launch("avatar") + flowWithResource { studentRepository.getSavedStudents(false) } + .onEach { resource -> + when (resource.status) { + Status.LOADING -> Timber.i("Loading student avatar data started") + Status.SUCCESS -> { + studentsWitSemesters = resource.data + showCurrentStudentAvatar() + } + Status.ERROR -> { + Timber.i("Loading student avatar result: An exception occurred") + errorHandler.dispatch(resource.error!!) + } + } + }.launch("avatar") } - fun onViewChange(destinationView: BaseView) { + fun onViewChange(section: MainView.Section?) { view?.apply { - showBottomNavigation(shouldShowBottomNavigation(destinationView)) + showActionBarElevation(section != GRADE && section != MESSAGE && section != SCHOOL) currentViewTitle?.let { setViewTitle(it) } currentViewSubtitle?.let { setViewSubTitle(it.ifBlank { null }) } currentStackSize?.let { @@ -106,13 +76,6 @@ class MainPresenter @Inject constructor( } } - private fun shouldShowBottomNavigation(destination: BaseView) = when (destination) { - is AccountView, - is StudentInfoView, - is AccountDetailsView -> false - else -> true - } - fun onAccountManagerSelected(): Boolean { if (studentsWitSemesters.isNullOrEmpty()) return true @@ -127,9 +90,12 @@ class MainPresenter @Inject constructor( return true } - fun onBackPressed() { + fun onBackPressed(default: () -> Unit) { Timber.i("Back pressed in main view") - view?.popView() + view?.run { + if (isRootView) default() + else popView() + } } fun onTabSelected(index: Int, wasSelected: Boolean): Boolean { @@ -147,52 +113,18 @@ class MainPresenter @Inject constructor( } == true } - fun onEnableAdsSelected() { - view?.showPrivacyPolicyDialog() - } - - fun onPrivacyAgree(isPersonalizedAds: Boolean) { - preferencesRepository.isAgreeToProcessData = true - preferencesRepository.isPersonalizedAdsEnabled = isPersonalizedAds - - adsHelper.initialize() - - preferencesRepository.isAdsEnabled = true - } - - fun onPrivacySelected() { - view?.openPrivacyPolicy() - } - private fun checkInAppReview() { - preferencesRepository.inAppReviewCount++ + prefRepository.inAppReviewCount++ - if (preferencesRepository.inAppReviewDate == null) { - preferencesRepository.inAppReviewDate = Instant.now() + if (prefRepository.inAppReviewDate == null) { + prefRepository.inAppReviewDate = LocalDate.now() } - if (!preferencesRepository.isAppReviewDone && preferencesRepository.inAppReviewCount >= 50 && - Instant.now().minus(Duration.ofDays(14)).isAfter(preferencesRepository.inAppReviewDate) + if (!prefRepository.isAppReviewDone && prefRepository.inAppReviewCount >= 50 && + LocalDate.now().minusDays(14).isAfter(prefRepository.inAppReviewDate) ) { view?.showInAppReview() - preferencesRepository.isAppReviewDone = true - } - } - - private fun checkAppSupport() { - if (!preferencesRepository.isAppSupportShown && !preferencesRepository.isAdsEnabled - && appInfo.buildFlavor == "play" - ) { - presenterScope.launch { - val student = runCatching { studentRepository.getCurrentStudent(false) } - .onFailure { Timber.e(it) } - .getOrElse { return@launch } - - if (Instant.now().minus(Duration.ofDays(28)).isAfter(student.registrationDate)) { - view?.showAppSupport() - preferencesRepository.isAppSupportShown = true - } - } + prefRepository.isAppReviewDone = true } } @@ -202,4 +134,10 @@ class MainPresenter @Inject constructor( view?.showStudentAvatar(currentStudent) } + + private fun getProperViewIndexes(initMenu: MainView.Section?) = when (initMenu?.id) { + in 0..3 -> initMenu!!.id to -1 + in 4..100 -> 4 to initMenu!!.id + else -> prefRepository.startMenuIndex to -1 + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt index 62436f3b..8851f587 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt @@ -3,11 +3,13 @@ package io.github.wulkanowy.ui.modules.main import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem interface MainView : BaseView { + var startMenuIndex: Int + + var startMenuMoreIndex: Int + val isRootView: Boolean val currentViewTitle: String? @@ -16,11 +18,7 @@ interface MainView : BaseView { val currentStackSize: Int? - fun initView( - startMenuIndex: Int, - rootAppMenuItems: List, - rootUpdatedDestinations: List - ) + fun initView() fun switchMenuView(position: Int) @@ -28,7 +26,7 @@ interface MainView : BaseView { fun showAccountPicker(studentWithSemesters: List) - fun showBottomNavigation(show: Boolean) + fun showActionBarElevation(show: Boolean) fun notifyMenuViewReselected() @@ -44,14 +42,6 @@ interface MainView : BaseView { fun showInAppReview() - fun showAppSupport() - - fun showPrivacyPolicyDialog() - - fun openPrivacyPolicy() - - fun openMoreDestination(destination: Destination) - interface MainChildView { fun onFragmentReselected() @@ -67,4 +57,25 @@ interface MainView : BaseView { get() = "" set(_) {} } + + enum class Section { + DASHBOARD, + GRADE, + ATTENDANCE, + TIMETABLE, + MORE, + MESSAGE, + EXAM, + HOMEWORK, + NOTE, + CONFERENCE, + SCHOOL_ANNOUNCEMENT, + SCHOOL, + LUCKY_NUMBER, + ACCOUNT, + STUDENT_INFO, + SETTINGS; + + val id get() = ordinal + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt index 4317fb7f..72fc627f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt @@ -4,18 +4,14 @@ import android.os.Bundle import android.view.View import android.view.View.INVISIBLE import android.view.View.VISIBLE -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.core.view.updateLayoutParams -import androidx.core.view.updateMargins -import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.enums.MessageFolder.* +import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED +import io.github.wulkanowy.data.enums.MessageFolder.SENT +import io.github.wulkanowy.data.enums.MessageFolder.TRASHED import io.github.wulkanowy.databinding.FragmentMessageBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment @@ -25,18 +21,12 @@ import javax.inject.Inject @AndroidEntryPoint class MessageFragment : BaseFragment(R.layout.fragment_message), - MessageView, MainView.TitledView, MainView.MainChildView { + MessageView, MainView.TitledView { @Inject lateinit var presenter: MessagePresenter - private val pagerAdapter by lazy { - BaseFragmentPagerAdapter( - fragmentManager = childFragmentManager, - pagesCount = 3, - lifecycle = lifecycle, - ) - } + private val pagerAdapter by lazy { BaseFragmentPagerAdapter(childFragmentManager) } companion object { fun newInstance() = MessageFragment() @@ -53,34 +43,26 @@ class MessageFragment : BaseFragment(R.layout.fragment_m } override fun initView() { + with(pagerAdapter) { + containerId = binding.messageViewPager.id + addFragmentsWithTitle(mapOf( + MessageTabFragment.newInstance(RECEIVED) to getString(R.string.message_inbox), + MessageTabFragment.newInstance(SENT) to getString(R.string.message_sent), + MessageTabFragment.newInstance(TRASHED) to getString(R.string.message_trash) + )) + } + with(binding.messageViewPager) { adapter = pagerAdapter offscreenPageLimit = 2 setOnSelectPageListener(presenter::onPageSelected) } - with(pagerAdapter) { - containerId = binding.messageViewPager.id - titleFactory = { - when (it) { - 0 -> getString(R.string.message_inbox) - 1 -> getString(R.string.message_sent) - 2 -> getString(R.string.message_trash) - else -> throw IllegalStateException() - } - } - itemFactory = { - when (it) { - 0 -> MessageTabFragment.newInstance(RECEIVED) - 1 -> MessageTabFragment.newInstance(SENT) - 2 -> MessageTabFragment.newInstance(TRASHED) - else -> throw IllegalStateException() - } - } - TabLayoutMediator(binding.messageTabLayout, binding.messageViewPager, this).attach() + with(binding.messageTabLayout) { + setupWithViewPager(binding.messageViewPager) + setElevationCompat(context.dpToPx(4f)) } - binding.messageTabLayout.elevation = requireContext().dpToPx(4f) binding.openSendMessageButton.setOnClickListener { presenter.onSendMessageButtonClicked() } } @@ -95,68 +77,22 @@ class MessageFragment : BaseFragment(R.layout.fragment_m binding.messageProgress.visibility = if (show) VISIBLE else INVISIBLE } - override fun showNewMessage(show: Boolean) { - binding.openSendMessageButton.run { - if (show) show() else hide() - } - } - - override fun showTabLayout(show: Boolean) { - binding.messageTabLayout.isVisible = show - - with(binding.messageViewPager) { - isUserInputEnabled = show - updateLayoutParams { - updateMargins(top = if (show) requireContext().dpToPx(48f).toInt() else 0) - } - } - } - - fun onChildFragmentShowActionMode(show: Boolean) { - presenter.onChildViewShowActionMode(show) - } - fun onChildFragmentLoaded() { presenter.onChildViewLoaded() } - fun onChildFragmentShowNewMessage(show: Boolean) { - presenter.onChildViewShowNewMessage(show) - } - - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onFragmentReselected() - } - - override fun onFragmentChanged() { - if (::presenter.isInitialized) presenter.onFragmentChanged() + override fun notifyChildMessageDeleted(tabId: Int) { + (pagerAdapter.getFragmentInstance(tabId) as? MessageTabFragment)?.onParentDeleteMessage() } override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) { - (pagerAdapter.getFragmentInstance(index) as? MessageTabFragment) - ?.onParentLoadData(forceRefresh) - } - - override fun notifyChildrenFinishActionMode() { - repeat(3) { - (pagerAdapter.getFragmentInstance(it) as? MessageTabFragment) - ?.onParentFinishActionMode() - } - } - - override fun notifyChildParentReselected(index: Int) { - (pagerAdapter.getFragmentInstance(index) as? MessageTabFragment) - ?.onParentReselected() + (pagerAdapter.getFragmentInstance(index) as? MessageTabFragment)?.onParentLoadData(forceRefresh) } override fun openSendMessage() { context?.let { it.startActivity(SendMessageActivity.getStartIntent(it)) } } - override fun popView() { - (activity as? MainActivity)?.popView() - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt index cf6bad19..7b8c3d0f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt @@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.message import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -14,7 +15,8 @@ class MessagePresenter @Inject constructor( override fun onAttachView(view: MessageView) { super.onAttachView(view) - presenterScope.launch { + launch { + delay(150) view.initView() Timber.i("Message view was initialized") loadData() @@ -23,7 +25,6 @@ class MessagePresenter @Inject constructor( fun onPageSelected(index: Int) { loadChild(index) - view?.notifyChildrenFinishActionMode() } private fun loadData() { @@ -35,18 +36,6 @@ class MessagePresenter @Inject constructor( view?.notifyChildLoadData(index, forceRefresh) } - fun onFragmentChanged() { - view?.notifyChildrenFinishActionMode() - } - - fun onFragmentReselected() { - Timber.i("Message view is reselected") - view?.run { - popView() - notifyChildParentReselected(currentPageIndex) - } - } - fun onChildViewLoaded() { view?.apply { showContent(true) @@ -54,14 +43,6 @@ class MessagePresenter @Inject constructor( } } - fun onChildViewShowNewMessage(show: Boolean) { - view?.showNewMessage(show) - } - - fun onChildViewShowActionMode(show: Boolean) { - view?.showTabLayout(!show) - } - fun onSendMessageButtonClicked() { view?.openSendMessage() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt index def4a275..2aa4d78e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt @@ -12,17 +12,9 @@ interface MessageView : BaseView { fun showProgress(show: Boolean) - fun showNewMessage(show: Boolean) - - fun showTabLayout(show: Boolean) - fun notifyChildLoadData(index: Int, forceRefresh: Boolean) - fun notifyChildrenFinishActionMode() - - fun notifyChildParentReselected(index: Int) + fun notifyChildMessageDeleted(tabId: Int) fun openSendMessage() - - fun popView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt deleted file mode 100644 index 59f6d288..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.mailboxchooser - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.recyclerview.widget.DiffUtil.ItemCallback -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.data.db.entities.MailboxType -import io.github.wulkanowy.databinding.ItemMailboxChooserBinding -import javax.inject.Inject - -class MailboxChooserAdapter @Inject constructor() : - ListAdapter(Differ) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( - ItemMailboxChooserBinding.inflate( - LayoutInflater.from(parent.context), parent, false - ) - ) - - override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { - holder.bind(getItem(position)) - } - - class ItemViewHolder( - private val binding: ItemMailboxChooserBinding, - ) : RecyclerView.ViewHolder(binding.root) { - - fun bind(item: MailboxChooserItem) { - with(binding) { - mailboxItemName.text = item.mailbox?.getFirstLine() - ?: root.resources.getString(R.string.message_chip_all_mailboxes) - mailboxItemSchool.text = item.mailbox?.getSecondLine() - mailboxItemSchool.isVisible = !item.isAll - - root.setOnClickListener { item.onClickListener(item.mailbox) } - } - } - - private fun Mailbox.getFirstLine() = buildString { - if (studentName.isNotBlank() && studentName != userName) { - append(studentName) - append(" - ") - } - append(userName) - } - - private fun Mailbox.getSecondLine() = buildString { - append(schoolNameShort) - append(" - ") - append(getMailboxType(type)) - } - - private fun getMailboxType(type: MailboxType): String = when (type) { - MailboxType.STUDENT -> R.string.message_mailbox_type_student - MailboxType.PARENT -> R.string.message_mailbox_type_parent - MailboxType.GUARDIAN -> R.string.message_mailbox_type_guardian - MailboxType.EMPLOYEE -> R.string.message_mailbox_type_employee - MailboxType.UNKNOWN -> null - }.let { it?.let { it1 -> binding.root.resources.getString(it1) }.orEmpty() } - } - - private object Differ : ItemCallback() { - override fun areItemsTheSame( - oldItem: MailboxChooserItem, - newItem: MailboxChooserItem - ): Boolean { - return oldItem.mailbox?.globalKey == newItem.mailbox?.globalKey - } - - override fun areContentsTheSame( - oldItem: MailboxChooserItem, - newItem: MailboxChooserItem - ): Boolean { - return oldItem == newItem - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt deleted file mode 100644 index 8bd84f2b..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt +++ /dev/null @@ -1,75 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.mailboxchooser - -import android.app.Dialog -import android.os.Bundle -import android.view.View -import androidx.core.os.bundleOf -import androidx.fragment.app.setFragmentResult -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.databinding.DialogMailboxChooserBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.parcelableArray -import javax.inject.Inject - -@AndroidEntryPoint -class MailboxChooserDialog : BaseDialogFragment(), MailboxChooserView { - - @Inject - lateinit var presenter: MailboxChooserPresenter - - @Inject - lateinit var mailboxAdapter: MailboxChooserAdapter - - companion object { - const val LISTENER_KEY = "mailbox_selected" - const val MAILBOX_KEY = "selected_mailbox" - const val REQUIRED_KEY = "is_mailbox_required" - - fun newInstance(mailboxes: List, isMailboxRequired: Boolean, folder: String) = - MailboxChooserDialog().apply { - arguments = bundleOf( - MAILBOX_KEY to mailboxes.toTypedArray(), - REQUIRED_KEY to isMailboxRequired, - LISTENER_KEY to folder, - ) - } - } - - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView( - DialogMailboxChooserBinding.inflate(layoutInflater).apply { binding = this }.root - ) - .create() - } - - - @Suppress("UNCHECKED_CAST") - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - presenter.onAttachView( - view = this, - requireMailbox = requireArguments().getBoolean(REQUIRED_KEY, false), - mailboxes = requireArguments().parcelableArray(MAILBOX_KEY).orEmpty().toList(), - ) - } - - override fun initView() { - binding.accountQuickDialogRecycler.adapter = mailboxAdapter - } - - override fun submitData(items: List) { - mailboxAdapter.submitList(items) - } - - override fun onMailboxSelected(item: Mailbox?) { - setFragmentResult( - requestKey = requireArguments().getString(LISTENER_KEY).orEmpty(), - result = bundleOf(MAILBOX_KEY to item), - ) - dismiss() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt deleted file mode 100644 index 6923cf08..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.mailboxchooser - -import io.github.wulkanowy.data.db.entities.Mailbox - -data class MailboxChooserItem( - val mailbox: Mailbox? = null, - val isAll: Boolean = false, - val onClickListener: (Mailbox?) -> Unit, -) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt deleted file mode 100644 index 5bd7c84a..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.mailboxchooser - -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import timber.log.Timber -import javax.inject.Inject - -class MailboxChooserPresenter @Inject constructor( - errorHandler: ErrorHandler, - studentRepository: StudentRepository -) : BasePresenter(errorHandler, studentRepository) { - - fun onAttachView(view: MailboxChooserView, mailboxes: List, requireMailbox: Boolean) { - super.onAttachView(view) - - view.initView() - Timber.i("Mailbox chooser view was initialized") - view.submitData(getMailboxItems(mailboxes, requireMailbox)) - } - - private fun getMailboxItems( - mailboxes: List, - requireMailbox: Boolean, - ): List = buildList { - if (!requireMailbox) { - add(MailboxChooserItem(isAll = true, onClickListener = ::onMailboxSelect)) - } - addAll(mailboxes.map { - MailboxChooserItem(mailbox = it, isAll = false, onClickListener = ::onMailboxSelect) - }) - } - - fun onMailboxSelect(item: Mailbox?) { - view?.onMailboxSelected(item) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt deleted file mode 100644 index 2e20ee81..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.mailboxchooser - -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.ui.base.BaseView - -interface MailboxChooserView : BaseView { - - fun initView() - - fun submitData(items: List) - - fun onMailboxSelected(item: Mailbox?) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt index d3c6b95c..421453c9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt @@ -1,10 +1,9 @@ package io.github.wulkanowy.ui.modules.message.preview +import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT -import androidx.core.text.parseAsHtml import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message @@ -73,33 +72,37 @@ class MessagePreviewAdapter @Inject constructor() : } } + @SuppressLint("SetTextI18n") private fun bindMessage(holder: MessageViewHolder, message: Message) { val context = holder.binding.root.context - val recipientCount = (message.unreadBy ?: 0) + (message.readBy ?: 0) - val isReceived = message.unreadBy == null + val recipientCount = message.unreadBy + message.readBy val readText = when { recipientCount > 1 -> { - context.getString(R.string.message_read_by, message.readBy, recipientCount) + context.resources.getQuantityString( + R.plurals.message_read_by, + message.readBy, + message.readBy, + recipientCount + ) } - message.readBy == 1 || (isReceived && !message.unread) -> { + message.readBy == 1 -> { context.getString(R.string.message_read, context.getString(R.string.all_yes)) } else -> context.getString(R.string.message_read, context.getString(R.string.all_no)) } with(holder.binding) { - messagePreviewSubject.text = message.subject.ifBlank { - context.getString(R.string.message_no_subject) - } - messagePreviewDate.text = context.getString( + messagePreviewSubject.text = + message.subject.ifBlank { root.context.getString(R.string.message_no_subject) } + messagePreviewDate.text = root.context.getString( R.string.message_date, message.date.toFormattedString("yyyy-MM-dd HH:mm:ss") ) messagePreviewRead.text = readText - messagePreviewContent.text = message.content.parseAsHtml(FROM_HTML_MODE_COMPACT) + messagePreviewContent.text = message.content messagePreviewFromSender.text = message.sender - messagePreviewToRecipient.text = message.recipients + messagePreviewToRecipient.text = message.recipient } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 6c54d9fc..74f8f57e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.message.preview +import android.os.Build import android.os.Bundle import android.print.PrintAttributes import android.print.PrintManager @@ -12,8 +13,8 @@ import android.view.View.VISIBLE import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient +import androidx.annotation.RequiresApi import androidx.core.content.getSystemService -import androidx.core.os.bundleOf import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -24,7 +25,7 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity -import io.github.wulkanowy.utils.serializable +import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.shareText import javax.inject.Inject @@ -39,6 +40,9 @@ class MessagePreviewFragment : @Inject lateinit var previewAdapter: MessagePreviewAdapter + @Inject + lateinit var appInfo: AppInfo + private var menuReplyButton: MenuItem? = null private var menuForwardButton: MenuItem? = null @@ -59,8 +63,7 @@ class MessagePreviewFragment : get() = getString(R.string.message_no_subject) override val printHTML: String - get() = requireContext().assets.open("message-print-page.html").bufferedReader() - .use { it.readText() } + get() = requireContext().assets.open("message-print-page.html").bufferedReader().use { it.readText() } override val messageNotExists: String get() = getString(R.string.message_not_exists) @@ -68,12 +71,13 @@ class MessagePreviewFragment : companion object { const val MESSAGE_ID_KEY = "message_id" - fun newInstance(message: Message) = MessagePreviewFragment().apply { - arguments = bundleOf(MESSAGE_ID_KEY to message) + fun newInstance(message: Message): MessagePreviewFragment { + return MessagePreviewFragment().apply { + arguments = Bundle().apply { putSerializable(MESSAGE_ID_KEY, message) } + } } } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -83,10 +87,7 @@ class MessagePreviewFragment : super.onViewCreated(view, savedInstanceState) binding = FragmentMessagePreviewBinding.bind(view) messageContainer = binding.messagePreviewContainer - presenter.onAttachView( - view = this, - message = (savedInstanceState ?: arguments)?.serializable(MESSAGE_ID_KEY), - ) + presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as? Message) } override fun initView() { @@ -106,8 +107,6 @@ class MessagePreviewFragment : menuShareButton = menu.findItem(R.id.messagePreviewMenuShare) menuPrintButton = menu.findItem(R.id.messagePreviewMenuPrint) presenter.onCreateOptionsMenu() - - menu.findItem(R.id.mainMenuAccount).isVisible = false } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -136,12 +135,12 @@ class MessagePreviewFragment : binding.messagePreviewRecycler.visibility = if (show) VISIBLE else GONE } - override fun showOptions(show: Boolean, isReplayable: Boolean) { - menuReplyButton?.isVisible = isReplayable + override fun showOptions(show: Boolean) { + menuReplyButton?.isVisible = show menuForwardButton?.isVisible = show menuDeleteButton?.isVisible = show menuShareButton?.isVisible = show - menuPrintButton?.isVisible = show + menuPrintButton?.isVisible = show && appInfo.systemVersion >= Build.VERSION_CODES.LOLLIPOP } override fun setDeletedOptionsLabels() { @@ -149,7 +148,7 @@ class MessagePreviewFragment : } override fun setNotDeletedOptionsLabels() { - menuDeleteButton?.setTitle(R.string.message_move_to_trash) + menuDeleteButton?.setTitle(R.string.message_move_to_bin) } override fun showErrorView(show: Boolean) { @@ -176,12 +175,12 @@ class MessagePreviewFragment : context?.shareText(text, subject) } + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) override fun printDocument(html: String, jobName: String) { val webView = WebView(requireContext()) webView.webViewClient = object : WebViewClient() { - override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = - false + override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false override fun onPageFinished(view: WebView, url: String) { createWebPrintJob(view, jobName) @@ -191,6 +190,7 @@ class MessagePreviewFragment : webView.loadDataWithBaseURL("file:///android_asset/", html, "text/HTML", "UTF-8", null) } + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) private fun createWebPrintJob(webView: WebView, jobName: String) { activity?.getSystemService()?.let { printManager -> val printAdapter = webView.createPrintDocumentAdapter(jobName) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index 56f23b6f..702e5467 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -1,8 +1,8 @@ package io.github.wulkanowy.ui.modules.message.preview import android.annotation.SuppressLint -import androidx.core.text.parseAsHtml -import io.github.wulkanowy.data.* +import android.os.Build +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.MessageAttachment import io.github.wulkanowy.data.enums.MessageFolder @@ -11,8 +11,12 @@ import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource +import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.toFormattedString -import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -20,7 +24,8 @@ class MessagePreviewPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, - private val analytics: AnalyticsHelper + private val analytics: AnalyticsHelper, + private var appInfo: AppInfo ) : BasePresenter(errorHandler, studentRepository) { var message: Message? = null @@ -51,43 +56,44 @@ class MessagePreviewPresenter @Inject constructor( view?.showErrorDetailsDialog(lastError) } - private fun loadData(messageToLoad: Message) { - flatResourceFlow { - val student = studentRepository.getCurrentStudent() - messageRepository.getMessage(student, messageToLoad, true) - } - .logResourceStatus("message ${messageToLoad.messageId} preview") - .onResourceData { - if (it != null) { - message = it.message - attachments = it.attachments - view?.apply { - setMessageWithAttachment(it) - showContent(true) - initOptions() - } - } else { - view?.run { - showMessage(messageNotExists) - popView() + private fun loadData(message: Message) { + flowWithResourceIn { + val student = studentRepository.getStudentById(message.studentId) + messageRepository.getMessage(student, message, true) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading message ${message.messageId} preview started") + Status.SUCCESS -> { + Timber.i("Loading message ${message.messageId} preview result: Success ") + if (it.data != null) { + this@MessagePreviewPresenter.message = it.data.message + this@MessagePreviewPresenter.attachments = it.data.attachments + view?.apply { + setMessageWithAttachment(it.data) + showContent(true) + initOptions() + } + analytics.logEvent( + "load_item", + "type" to "message_preview", + "length" to it.data.message.content.length + ) + } else { + view?.run { + showMessage(messageNotExists) + popView() + } } } - } - .onResourceSuccess { - if (it != null) { - analytics.logEvent( - "load_item", - "type" to "message_preview", - "length" to it.message.content.length - ) + Status.ERROR -> { + Timber.i("Loading message ${message.messageId} preview result: An exception occurred ") + retryCallback = { onMessageLoadRetry(message) } + errorHandler.dispatch(it.error!!) } } - .onResourceNotLoading { view?.showProgress(false) } - .onResourceError { - retryCallback = { onMessageLoadRetry(messageToLoad) } - errorHandler.dispatch(it) - } - .launch() + }.afterLoading { + view?.showProgress(false) + }.launch() } fun onReply(): Boolean { @@ -105,69 +111,57 @@ class MessagePreviewPresenter @Inject constructor( } fun onShare(): Boolean { - val message = message ?: return false - val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() } + message?.let { + var text = "Temat: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}\n" + when (it.sender.isNotEmpty()) { + true -> "Od: ${it.sender}\n" + false -> "Do: ${it.recipient}\n" + } + "Data: ${it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${it.content}" - val text = buildString { - appendLine("Temat: $subject") - appendLine("Od: ${message.sender}") - appendLine("Do: ${message.recipients}") - appendLine("Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}") + attachments?.let { attachments -> + if (attachments.isNotEmpty()) { + text += "\n\nZałączniki:" - appendLine() - - appendLine(message.content.parseAsHtml()) - - if (!attachments.isNullOrEmpty()) { - appendLine() - appendLine("Załączniki:") - - append(attachments.orEmpty().joinToString(separator = "\n") { attachment -> - "${attachment.filename}: ${attachment.url}" - }) + attachments.forEach { attachment -> + text += "\n${attachment.filename}: ${attachment.url}" + } + } } - } - view?.shareText( - subject = "FW: $subject", - text = text, - ) - return true + view?.shareText(text, "FW: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}") + return true + } + return false } @SuppressLint("NewApi") fun onPrint(): Boolean { - val message = message ?: return false - val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() } + if (appInfo.systemVersion < Build.VERSION_CODES.LOLLIPOP) return false + message?.let { + val dateString = it.date.toFormattedString("yyyy-MM-dd HH:mm:ss") + val infoContent = "

Data wysłania

$dateString
" + when { + it.sender.isNotEmpty() -> "

Od

${it.sender}
" + else -> "

Do

${it.recipient}
" + } - val dateString = message.date.toFormattedString("yyyy-MM-dd HH:mm:ss") + val messageContent = "

${it.content}

" + .replace(Regex("[\\n\\r]{2,}"), "

") + .replace(Regex("[\\n\\r]"), "
") - val infoContent = buildString { - append("

Data wysłania

$dateString
") + val jobName = "Wiadomość " + when { + it.sender.isNotEmpty() -> "od ${it.sender}" + else -> "do ${it.recipient}" + } + " $dateString: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }} | Wulkanowy" - append("

Od

${message.sender}
") - append("

DO

${message.recipients}
") + view?.apply { + val html = printHTML + .replace("%SUBJECT%", it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }) + .replace("%CONTENT%", messageContent) + .replace("%INFO%", infoContent) + printDocument(html, jobName) + } + return true } - val messageContent = "

${message.content}

" - .replace(Regex("[\\n\\r]{2,}"), "

") - .replace(Regex("[\\n\\r]"), "
") - - val jobName = buildString { - append("Wiadomość ") - append("od ${message.correspondents}") - append("do ${message.correspondents}") - append(" $dateString: $subject | Wulkanowy") - } - - view?.apply { - val html = printHTML - .replace("%SUBJECT%", subject) - .replace("%CONTENT%", messageContent) - .replace("%INFO%", infoContent) - printDocument(html, jobName) - } - - return true + return false } private fun deleteMessage() { @@ -176,31 +170,32 @@ class MessagePreviewPresenter @Inject constructor( view?.run { showContent(false) showProgress(true) - showOptions(show = false, isReplayable = false) + showOptions(false) showErrorView(false) } - Timber.i("Delete message ${message?.messageGlobalKey}") - - presenterScope.launch { - runCatching { - val student = studentRepository.getCurrentStudent(decryptPass = true) - val mailbox = messageRepository.getMailboxByStudent(student) - messageRepository.deleteMessage(student, mailbox, message!!) - } - .onFailure { - retryCallback = { onMessageDelete() } - errorHandler.dispatch(it) - } - .onSuccess { + flowWithResource { + val student = studentRepository.getCurrentStudent() + messageRepository.deleteMessage(student, message!!) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.d("Message ${message?.id} delete started") + Status.SUCCESS -> { + Timber.d("Message ${message?.id} delete success") view?.run { showMessage(deleteMessageSuccessString) popView() } } - + Status.ERROR -> { + Timber.d("Message ${message?.id} delete failed") + retryCallback = { onMessageDelete() } + errorHandler.dispatch(it.error!!) + } + } + }.afterLoading { view?.showProgress(false) - } + }.launch("delete") } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -220,10 +215,7 @@ class MessagePreviewPresenter @Inject constructor( private fun initOptions() { view?.apply { - showOptions( - show = message != null, - isReplayable = message?.folderId != MessageFolder.SENT.id, - ) + showOptions(message != null) message?.let { when (it.folderId == MessageFolder.TRASHED.id) { true -> setDeletedOptionsLabels() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt index c5a94793..583ba687 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -1,5 +1,7 @@ package io.github.wulkanowy.ui.modules.message.preview +import android.os.Build +import androidx.annotation.RequiresApi import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.ui.base.BaseView @@ -28,7 +30,7 @@ interface MessagePreviewView : BaseView { fun setErrorRetryCallback(callback: () -> Unit) - fun showOptions(show: Boolean, isReplayable: Boolean) + fun showOptions(show: Boolean) fun setDeletedOptionsLabels() @@ -40,7 +42,8 @@ interface MessagePreviewView : BaseView { fun shareText(text: String, subject: String) - fun popView() - + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) fun printDocument(html: String, jobName: String) + + fun popView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChipItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChipItem.kt index bd14bc89..26ab7f48 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChipItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/RecipientChipItem.kt @@ -1,10 +1,10 @@ package io.github.wulkanowy.ui.modules.message.send +import com.squareup.moshi.JsonClass import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.materialchipsinput.ChipItem -import kotlinx.serialization.Serializable -@Serializable +@JsonClass(generateAdapter = true) data class RecipientChipItem( override val title: String, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt index 0ba82f1a..1432a994 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt @@ -1,37 +1,27 @@ package io.github.wulkanowy.ui.modules.message.send import android.annotation.SuppressLint +import android.app.AlertDialog import android.content.Context import android.content.Intent import android.graphics.Rect -import android.os.Build import android.os.Bundle -import android.text.Spanned import android.view.Menu import android.view.MenuItem import android.view.TouchDelegate import android.view.View.GONE import android.view.View.VISIBLE -import android.view.ViewGroup import android.widget.Toast import android.widget.Toast.LENGTH_LONG -import androidx.core.text.parseAsHtml -import androidx.core.text.toHtml -import androidx.core.view.* import androidx.core.widget.doOnTextChanged -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.databinding.ActivitySendMessageBinding import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog -import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.LISTENER_KEY -import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.MAILBOX_KEY import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.nullableSerializable import io.github.wulkanowy.utils.showSoftInput import javax.inject.Inject @@ -82,50 +72,24 @@ class SendMessageActivity : BaseActivity= Build.VERSION_CODES.R) { - WindowCompat.setDecorFitsSystemWindows(window, false) - binding.sendAppBar.isLifted = true - } - initializeMessageContainer() - messageContainer = binding.sendMessageContainer formRecipientsData = binding.sendMessageTo.addedChipItems as List formSubjectValue = binding.sendMessageSubject.text.toString() - formContentValue = - binding.sendMessageMessageContent.text.toString().parseAsHtml().toString() - binding.sendMessageFrom.setOnClickListener { presenter.onOpenMailboxChooser() } + formContentValue = binding.sendMessageMessageContent.text.toString() presenter.onAttachView( view = this, - reason = intent.nullableSerializable(EXTRA_REASON), - message = intent.nullableSerializable(EXTRA_MESSAGE), - reply = intent.nullableSerializable(EXTRA_REPLY) + reason = intent.getSerializableExtra(EXTRA_REASON) as? String, + message = intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, + reply = intent.getSerializableExtra(EXTRA_REPLY) as? Boolean ) - supportFragmentManager.setFragmentResultListener(LISTENER_KEY, this) { _, bundle -> - presenter.onMailboxSelected(bundle.nullableSerializable(MAILBOX_KEY)) - } } @SuppressLint("ClickableViewAccessibility") @@ -140,29 +104,13 @@ class SendMessageActivity : BaseActivity - val navigationBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) - val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()) - - view.updateLayoutParams { - bottomMargin = if (imeInsets.bottom > navigationBarInsets.bottom) { - imeInsets.bottom - } else { - navigationBarInsets.bottom - } - } - WindowInsetsCompat.CONSUMED - } - } - private fun onMessageSubjectChange(text: CharSequence?) { formSubjectValue = text.toString() presenter.onMessageContentChange() } private fun onMessageContentChange(text: CharSequence?) { - formContentValue = (text as Spanned).toHtml() + formContentValue = text.toString() presenter.onMessageContentChange() } @@ -170,7 +118,7 @@ class SendMessageActivity : BaseActivity) { @@ -217,7 +165,7 @@ class SendMessageActivity : BaseActivity) { - MailboxChooserDialog.newInstance( - mailboxes = mailboxes, - isMailboxRequired = true, - folder = LISTENER_KEY, - ).show(supportFragmentManager, "chooser") - } - override fun popView() { finish() } @@ -278,7 +218,7 @@ class SendMessageActivity : BaseActivity presenter.restoreMessageParts() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt index e776e994..60a23e58 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -1,22 +1,25 @@ package io.github.wulkanowy.ui.modules.message.send -import io.github.wulkanowy.data.* -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.data.db.entities.MailboxType +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.pojos.MessageDraft import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.RecipientRepository +import io.github.wulkanowy.data.repositories.ReportingUnitRepository +import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.onEach @@ -27,7 +30,9 @@ import javax.inject.Inject class SendMessagePresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, private val messageRepository: MessageRepository, + private val reportingUnitRepository: ReportingUnitRepository, private val recipientRepository: RecipientRepository, private val preferencesRepository: PreferencesRepository, private val analytics: AnalyticsHelper @@ -35,19 +40,10 @@ class SendMessagePresenter @Inject constructor( private val messageUpdateChannel = Channel() - private var message: Message? = null - private var isReplay: Boolean? = null - - private var mailboxes: List = emptyList() - private var selectedMailbox: Mailbox? = null - fun onAttachView(view: SendMessageView, reason: String?, message: Message?, reply: Boolean?) { super.onAttachView(view) view.initView() initializeSubjectStream() - this.message = message - this.isReplay = reply - Timber.i("Send message view was initialized") loadData(message, reply) with(view) { @@ -55,27 +51,23 @@ class SendMessagePresenter @Inject constructor( view.showMessageBackupDialog() } reason?.let { - setSubject("Usprawiedliwienie") + setSubject("Usprawiedliwenie") setContent(it) } message?.let { - setSubject( - when (reply) { - true -> "RE: " - else -> "FW: " - } + message.subject - ) + setSubject(when (reply) { + true -> "RE: " + else -> "FW: " + } + message.subject) if (preferencesRepository.fillMessageContent || reply != true) { - setContent(buildString { - if (reply == true) { - append("

") - } - - append("Od: ${message.sender}
") - append("Do: ${message.recipients}
") - append("Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}

") - append(message.content) - }) + setContent( + when (reply) { + true -> "\n\n" + else -> "" + } + when (message.sender.isNotEmpty()) { + true -> "Od: ${message.sender}\n" + false -> "Do: ${message.recipient}\n" + } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}") } } } @@ -118,102 +110,73 @@ class SendMessagePresenter @Inject constructor( return false } - fun onOpenMailboxChooser() { - view?.showMailboxChooser(mailboxes) - } - - fun onMailboxSelected(mailbox: Mailbox?) { - selectedMailbox = mailbox - - loadData(message, isReplay) - } - private fun loadData(message: Message?, reply: Boolean?) { - resourceFlow { + flowWithResource { val student = studentRepository.getCurrentStudent() - - if (selectedMailbox == null && mailboxes.isEmpty()) { - selectedMailbox = messageRepository.getMailboxByStudent(student) - mailboxes = messageRepository.getMailboxes(student, false).toFirstResult() - .dataOrNull.orEmpty() - } + val semester = semesterRepository.getCurrentSemester(student) + val unit = reportingUnitRepository.getReportingUnit(student, semester.unitId) Timber.i("Loading recipients started") - val recipients = createChips( - recipients = recipientRepository.getRecipients( - student = student, - mailbox = selectedMailbox, - type = MailboxType.EMPLOYEE, - ) - ) + val recipients = when { + unit != null -> recipientRepository.getRecipients(student, unit, 2) + else -> listOf() + }.let { createChips(it) } Timber.i("Loading recipients result: Success, fetched %d recipients", recipients.size) Timber.i("Loading message recipients started") val messageRecipients = when { - message != null && reply == true -> recipientRepository.getMessageSender( - student = student, - message = message, - mailbox = selectedMailbox, - ) + message != null && reply == true -> recipientRepository.getMessageRecipients(student, message) else -> emptyList() }.let { createChips(it) } - Timber.i( - "Loaded message recipients to reply result: Success, fetched %d recipients", - messageRecipients.size - ) + Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", messageRecipients.size) - recipients to messageRecipients - } - .logResourceStatus("load recipients") - .onResourceLoading { - view?.run { + Triple(unit, recipients, messageRecipients) + }.onEach { + when (it.status) { + Status.LOADING -> view?.run { + Timber.i("Loading recipients started") showProgress(true) showContent(false) } - } - .onResourceNotLoading { - view?.run { showProgress(false) } - } - .onResourceError { - view?.showContent(true) - errorHandler.dispatch(it) - } - .onResourceSuccess { - it.let { (recipientChips, selectedRecipientChips) -> + Status.SUCCESS -> it.data!!.let { (reportingUnit, recipientChips, selectedRecipientChips) -> view?.run { - setMailbox(getMailboxName(selectedMailbox)) - setRecipients(recipientChips) - if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients( - selectedRecipientChips - ) - showContent(true) + if (reportingUnit != null) { + setReportingUnit(reportingUnit) + setRecipients(recipientChips) + if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(selectedRecipientChips) + showContent(true) + } else { + Timber.i("Loading recipients result: Can't find the reporting unit") + view?.showEmpty(true) + } } } + Status.ERROR -> { + Timber.i("Loading recipients result: An exception occurred") + view?.showContent(true) + errorHandler.dispatch(it.error!!) + } } - .launch() + }.afterLoading { + view?.run { showProgress(false) } + }.launch() } private fun sendMessage(subject: String, content: String, recipients: List) { - val mailbox = selectedMailbox ?: return - - resourceFlow { + flowWithResource { val student = studentRepository.getCurrentStudent() - messageRepository.sendMessage( - student = student, - subject = subject, - content = content, - recipients = recipients, - mailboxId = mailbox.globalKey, - ) - }.logResourceStatus("sending message").onEach { - when (it) { - is Resource.Loading -> view?.run { + messageRepository.sendMessage(student, subject, content, recipients) + }.onEach { + when (it.status) { + Status.LOADING -> view?.run { + Timber.i("Sending message started") showSoftInput(false) showContent(false) showProgress(true) showActionBar(false) } - is Resource.Success -> { + Status.SUCCESS -> { + Timber.i("Sending message result: Success") view?.clearDraft() view?.run { showMessage(messageSuccess) @@ -221,69 +184,54 @@ class SendMessagePresenter @Inject constructor( } analytics.logEvent("send_message", "recipients" to recipients.size) } - is Resource.Error -> { + Status.ERROR -> { + Timber.i("Sending message result: An exception occurred") view?.run { showContent(true) showProgress(false) showActionBar(true) } - errorHandler.dispatch(it.error) + errorHandler.dispatch(it.error!!) } } }.launch("send") } private fun createChips(recipients: List): List { + fun generateCorrectSummary(recipientRealName: String): String { + val substring = recipientRealName.substringBeforeLast("-") + return when { + substring == recipientRealName -> recipientRealName + substring.indexOf("(") != -1 -> { + recipientRealName.indexOf("(") + .let { recipientRealName.substring(if (it != -1) it else 0) } + } + substring.indexOf("[") != -1 -> { + recipientRealName.indexOf("[") + .let { recipientRealName.substring(if (it != -1) it else 0) } + } + else -> recipientRealName.substringAfter("-") + }.trim() + } + return recipients.map { RecipientChipItem( - title = it.userName, - summary = buildString { - getMailboxType(it.type)?.let(::append) - if (isNotBlank()) append(" ") - - append("(${it.schoolShortName})") - }, + title = it.name, + summary = generateCorrectSummary(it.realName), recipient = it ) } } - private fun getMailboxName(mailbox: Mailbox?): String { - mailbox ?: return "" - - // username - accountType [\n student name - ] (school short name) - return buildString { - append(mailbox.userName) - append(" - ") - append(getMailboxType(mailbox.type)) - appendLine() - - if (mailbox.type == MailboxType.PARENT) { - append(mailbox.studentName) - append(" - ") - } - - append("(${mailbox.schoolNameShort})") - } - } - - private fun getMailboxType(type: MailboxType): String? = when (type) { - MailboxType.STUDENT -> view?.mailboxStudent - MailboxType.PARENT -> view?.mailboxParent - MailboxType.GUARDIAN -> view?.mailboxGuardian - MailboxType.EMPLOYEE -> view?.mailboxEmployee - MailboxType.UNKNOWN -> null - } - fun onMessageContentChange() { - presenterScope.launch { + launch { messageUpdateChannel.send(Unit) } } @OptIn(FlowPreview::class) private fun initializeSubjectStream() { - presenterScope.launch { + launch { messageUpdateChannel.consumeAsFlow() .debounce(250) .catch { Timber.e(it) } @@ -296,9 +244,9 @@ class SendMessagePresenter @Inject constructor( private fun saveDraftMessage() { messageRepository.draftMessage = MessageDraft( - recipients = view?.formRecipientsData!!, - subject = view?.formSubjectValue!!, - content = view?.formContentValue!!, + view?.formRecipientsData!!, + view?.formSubjectValue!!, + view?.formContentValue!! ) } @@ -311,8 +259,7 @@ class SendMessagePresenter @Inject constructor( } fun getRecipientsNames(): String { - return messageRepository.draftMessage?.recipients.orEmpty() - .joinToString { it.recipient.userName } + return messageRepository.draftMessage?.recipients.orEmpty().joinToString { it.recipient.name } } fun clearDraft() { @@ -320,7 +267,6 @@ class SendMessagePresenter @Inject constructor( Timber.i("Draft cleared!") } - fun getMessageBackupContent(recipients: String) = - if (recipients.isEmpty()) view?.getMessageBackupDialogString() + fun getMessageBackupContent(recipients: String) = if (recipients.isEmpty()) view?.getMessageBackupDialogString() else view?.getMessageBackupDialogStringWithRecipients(recipients) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt index e27a09d6..21b42e3e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.message.send -import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.ui.base.BaseView interface SendMessageView : BaseView { @@ -18,17 +18,9 @@ interface SendMessageView : BaseView { val messageSuccess: String - val mailboxStudent: String - - val mailboxParent: String - - val mailboxGuardian: String - - val mailboxEmployee: String - fun initView() - fun setMailbox(mailbox: String) + fun setReportingUnit(unit: ReportingUnit) fun setRecipients(recipients: List) @@ -61,5 +53,4 @@ interface SendMessageView : BaseView { fun getMessageBackupDialogStringWithRecipients(recipients: String): String fun clearDraft() - fun showMailboxChooser(mailboxes: List) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt index 9792c708..571cc6d5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt @@ -1,190 +1,137 @@ package io.github.wulkanowy.ui.modules.message.tab -import android.annotation.SuppressLint -import android.content.res.ColorStateList import android.graphics.Typeface import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import android.widget.CompoundButton import androidx.core.view.isVisible -import androidx.core.widget.ImageViewCompat import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.NO_POSITION import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.databinding.ItemMessageBinding import io.github.wulkanowy.databinding.ItemMessageChipsBinding -import io.github.wulkanowy.utils.getCompatColor -import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.toFormattedString import javax.inject.Inject class MessageTabAdapter @Inject constructor() : RecyclerView.Adapter() { - lateinit var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit + enum class ViewType { HEADER, ITEM } - lateinit var onLongItemClickListener: (MessageTabDataItem.MessageItem) -> Unit + var onItemClickListener: (Message, position: Int) -> Unit = { _, _ -> } + var onHeaderClickListener: (chip: CompoundButton, isChecked: Boolean) -> Unit = { _, _ -> } - lateinit var onHeaderClickListener: (CompoundButton, Boolean) -> Unit - - lateinit var onMailboxClickListener: () -> Unit - - lateinit var onChangesDetectedListener: () -> Unit + var onChangesDetectedListener = {} private var items = mutableListOf() + private var onlyUnread: Boolean? = null + private var onlyWithAttachments = false - fun submitData(data: List) { - val originalMessagesSize = items.count { it.viewType == MessageItemViewType.MESSAGE } - val newMessagesSize = data.count { it.viewType == MessageItemViewType.MESSAGE } - - if (originalMessagesSize != newMessagesSize) onChangesDetectedListener() - + fun setDataItems( + data: List, + onlyUnread: Boolean?, + onlyWithAttachments: Boolean + ) { + if (items.size != data.size) onChangesDetectedListener() val diffResult = DiffUtil.calculateDiff(MessageTabDiffUtil(items, data)) items = data.toMutableList() - + this.onlyUnread = onlyUnread + this.onlyWithAttachments = onlyWithAttachments diffResult.dispatchUpdatesTo(this) } - override fun getItemViewType(position: Int) = items[position].viewType.ordinal + override fun getItemViewType(position: Int): Int { + return when (position) { + 0 -> ViewType.HEADER.ordinal + else -> ViewType.ITEM.ordinal + } + } override fun getItemCount() = items.size override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val inflater = LayoutInflater.from(parent.context) - - return when (MessageItemViewType.values()[viewType]) { - MessageItemViewType.FILTERS -> HeaderViewHolder( - ItemMessageChipsBinding.inflate(inflater, parent, false) - ) - MessageItemViewType.MESSAGE -> ItemViewHolder( + return when (viewType) { + ViewType.ITEM.ordinal -> ItemViewHolder( ItemMessageBinding.inflate(inflater, parent, false) ) + ViewType.HEADER.ordinal -> HeaderViewHolder( + ItemMessageChipsBinding.inflate(inflater, parent, false) + ) + else -> throw IllegalStateException() } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { - is ItemViewHolder -> bindItemViewHolder(holder, position) - is HeaderViewHolder -> bindHeaderViewHolder(holder, position) - } - } + is ItemViewHolder -> { + val item = (items[position] as MessageTabDataItem.MessageItem).message - @SuppressLint("PrivateResource") - private fun bindHeaderViewHolder(holder: HeaderViewHolder, position: Int) { - val item = items[position] as MessageTabDataItem.FilterHeader - val context = holder.binding.root.context + with(holder.binding) { + val style = if (item.unread) Typeface.BOLD else Typeface.NORMAL - with(holder.binding) { - chipMailbox.text = - item.selectedMailbox ?: context.getString(R.string.message_chip_all_mailboxes) - chipMailbox.chipBackgroundColor = ColorStateList.valueOf( - if (item.selectedMailbox == null) { - context.getCompatColor(R.color.m3_elevated_chip_background_color) - } else context.getThemeAttrColor(R.attr.colorPrimary, 64) - ) - chipMailbox.setTextColor( - if (item.selectedMailbox == null) { - context.getThemeAttrColor(R.attr.colorOnSurfaceVariant) - } else context.getThemeAttrColor(R.attr.colorPrimary) - ) - chipMailbox.setOnClickListener { onMailboxClickListener() } + messageItemAuthor.run { + text = + if (item.folderId == MessageFolder.SENT.id) item.recipient else item.sender + setTypeface(null, style) + } + messageItemSubject.run { + text = + if (item.subject.isNotBlank()) item.subject else context.getString(R.string.message_no_subject) + setTypeface(null, style) + } + messageItemDate.run { + text = item.date.toFormattedString() + setTypeface(null, style) + } + messageItemAttachmentIcon.visibility = + if (item.hasAttachments) View.VISIBLE else View.GONE - if (item.onlyUnread == null) { - chipUnread.isVisible = false - } else { - chipUnread.isVisible = true - chipUnread.isChecked = item.onlyUnread - chipUnread.setOnCheckedChangeListener(onHeaderClickListener) - } - chipUnread.isEnabled = item.isEnabled - - chipAttachments.isEnabled = item.isEnabled - chipAttachments.isChecked = item.onlyWithAttachments - chipAttachments.setOnCheckedChangeListener(onHeaderClickListener) - } - } - - private fun bindItemViewHolder(holder: ItemViewHolder, position: Int) { - val item = (items[position] as MessageTabDataItem.MessageItem) - val message = item.message - - with(holder.binding) { - val normalFont = Typeface.create("sans-serif", Typeface.NORMAL) - val boldFont = Typeface.create("sans-serif-black", Typeface.NORMAL) - - val primaryColor = root.context.getThemeAttrColor(android.R.attr.textColorPrimary) - val secondaryColor = root.context.getThemeAttrColor(android.R.attr.textColorSecondary) - - val currentFont = if (message.unread) boldFont else normalFont - val currentTextColor = if (message.unread) primaryColor else secondaryColor - - with(messageItemAuthor) { - text = message.correspondents - setTextColor(currentTextColor) - typeface = currentFont - } - with(messageItemSubject) { - text = message.subject.ifBlank { context.getString(R.string.message_no_subject) } - setTextColor(currentTextColor) - typeface = currentFont - } - with(messageItemDate) { - text = message.date.toFormattedString() - setTextColor(currentTextColor) - typeface = currentFont - } - with(messageItemAttachmentIcon) { - ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(currentTextColor)) - isVisible = message.hasAttachments - } - messageItemUnreadIndicator.isVisible = message.unread - - root.setOnClickListener { - holder.bindingAdapterPosition.let { - if (it != RecyclerView.NO_POSITION) { - onItemClickListener(item, it) + root.setOnClickListener { + holder.bindingAdapterPosition.let { + if (it != NO_POSITION) onItemClickListener(item, it) + } } } } - - root.setOnLongClickListener { - onLongItemClickListener(item) - true - } - - with(messageItemCheckbox) { - isChecked = item.isSelected - isVisible = item.isActionMode + is HeaderViewHolder -> { + with(holder.binding) { + if (onlyUnread == null) chipUnread.isVisible = false + else { + chipUnread.isVisible = true + chipUnread.isChecked = onlyUnread!! + chipUnread.setOnCheckedChangeListener(onHeaderClickListener) + } + chipAttachments.isChecked = onlyWithAttachments + chipAttachments.setOnCheckedChangeListener(onHeaderClickListener) + } } } } class ItemViewHolder(val binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root) - class HeaderViewHolder(val binding: ItemMessageChipsBinding) : RecyclerView.ViewHolder(binding.root) private class MessageTabDiffUtil( private val old: List, private val new: List - ) : DiffUtil.Callback() { - + ) : + DiffUtil.Callback() { override fun getOldListSize(): Int = old.size override fun getNewListSize(): Int = new.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - val oldItem = old[oldItemPosition] - val newItem = new[newItemPosition] - - return if (oldItem is MessageTabDataItem.MessageItem && newItem is MessageTabDataItem.MessageItem) { - oldItem.message.messageGlobalKey == newItem.message.messageGlobalKey - } else { - oldItem.viewType == newItem.viewType - } + return old[oldItemPosition].id == new[newItemPosition].id } - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = - old[oldItemPosition] == new[newItemPosition] + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return old[oldItemPosition] == new[newItemPosition] + } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt index c0bd4170..4f51a936 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt @@ -2,20 +2,14 @@ package io.github.wulkanowy.ui.modules.message.tab import io.github.wulkanowy.data.db.entities.Message -sealed class MessageTabDataItem(val viewType: MessageItemViewType) { +sealed class MessageTabDataItem { + data class MessageItem(val message: Message) : MessageTabDataItem() { + override val id = message.id + } - data class MessageItem( - val message: Message, - val isSelected: Boolean, - val isActionMode: Boolean - ) : MessageTabDataItem(MessageItemViewType.MESSAGE) + object Header : MessageTabDataItem() { + override val id = Long.MIN_VALUE + } - data class FilterHeader( - val selectedMailbox: String?, - val onlyUnread: Boolean?, - val onlyWithAttachments: Boolean, - val isEnabled: Boolean - ) : MessageTabDataItem(MessageItemViewType.FILTERS) + abstract val id: Long } - -enum class MessageItemViewType { FILTERS, MESSAGE } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt index 592cbd60..54ee74eb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -3,32 +3,24 @@ package io.github.wulkanowy.ui.modules.message.tab import android.os.Bundle import android.view.Menu import android.view.MenuInflater -import android.view.MenuItem import android.view.View -import android.view.View.* +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import android.widget.CompoundButton -import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.SearchView -import androidx.core.os.bundleOf -import androidx.core.view.updatePadding -import androidx.fragment.app.setFragmentResultListener import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.databinding.FragmentMessageTabBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.message.MessageFragment -import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment import io.github.wulkanowy.ui.widgets.DividerItemDecoration -import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.nullableSerializable import javax.inject.Inject @AndroidEntryPoint @@ -39,52 +31,27 @@ class MessageTabFragment : BaseFragment(R.layout.frag lateinit var presenter: MessageTabPresenter @Inject - lateinit var messageTabAdapter: MessageTabAdapter + lateinit var tabAdapter: MessageTabAdapter companion object { - const val MESSAGE_TAB_FOLDER_ID = "message_tab_folder_id" - fun newInstance(folder: MessageFolder) = MessageTabFragment().apply { - arguments = bundleOf(MESSAGE_TAB_FOLDER_ID to folder.name) + fun newInstance(folder: MessageFolder): MessageTabFragment { + return MessageTabFragment().apply { + arguments = Bundle().apply { + putString(MESSAGE_TAB_FOLDER_ID, folder.name) + } + } } } override val isViewEmpty - get() = messageTabAdapter.itemCount == 0 + get() = tabAdapter.itemCount == 0 - private var actionMode: ActionMode? = null + override var onlyUnread: Boolean? = false - private val actionModeCallback = object : ActionMode.Callback { - override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { - val inflater = mode.menuInflater - inflater.inflate(R.menu.context_menu_message_tab, menu) - return true - } + override var onlyWithAttachments = false - override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - if (presenter.folder == MessageFolder.TRASHED) { - val menuItem = menu.findItem(R.id.messageTabContextMenuDelete) - menuItem.setTitle(R.string.message_delete_forever) - } - return presenter.onPrepareActionMode() - } - - override fun onDestroyActionMode(mode: ActionMode) { - presenter.onDestroyActionMode() - actionMode = null - } - - override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean { - when (menu.itemId) { - R.id.messageTabContextMenuDelete -> presenter.onActionModeSelectDelete() - R.id.messageTabContextMenuSelectAll -> presenter.onActionModeSelectCheckAll() - } - return true - } - } - - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -102,39 +69,30 @@ class MessageTabFragment : BaseFragment(R.layout.frag } override fun initView() { - with(messageTabAdapter) { + with(tabAdapter) { onItemClickListener = presenter::onMessageItemSelected - onLongItemClickListener = presenter::onMessageItemLongSelected onHeaderClickListener = ::onChipChecked - onMailboxClickListener = presenter::onMailboxFilterSelected onChangesDetectedListener = ::resetListPosition } with(binding.messageTabRecycler) { layoutManager = LinearLayoutManager(context) - adapter = messageTabAdapter + adapter = tabAdapter addItemDecoration(DividerItemDecoration(context, false)) - itemAnimator = null } - with(binding) { messageTabSwipe.setOnRefreshListener(presenter::onSwipeRefresh) messageTabSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) messageTabSwipe.setProgressBackgroundColorSchemeColor( - requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + requireContext().getThemeAttrColor( + R.attr.colorSwipeRefresh + ) ) messageTabErrorRetry.setOnClickListener { presenter.onRetry() } messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() } } - - setFragmentResultListener(requireArguments().getString(MESSAGE_TAB_FOLDER_ID)!!) { _, bundle -> - presenter.onMailboxSelected( - mailbox = bundle.nullableSerializable(MailboxChooserDialog.MAILBOX_KEY), - ) - } } - @Suppress("DEPRECATION") override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) inflater.inflate(R.menu.action_menu_message_tab, menu) @@ -151,28 +109,9 @@ class MessageTabFragment : BaseFragment(R.layout.frag }) } - override fun updateData(data: List) { - messageTabAdapter.submitData(data) - } - - override fun updateActionModeTitle(selectedMessagesSize: Int) { - actionMode?.title = resources.getQuantityString( - R.plurals.message_selected_messages_count, - selectedMessagesSize, - selectedMessagesSize - ) - } - - override fun updateSelectAllMenu(isAllSelected: Boolean) { - val menuItem = actionMode?.menu?.findItem(R.id.messageTabContextMenuSelectAll) ?: return - - if (isAllSelected) { - menuItem.setTitle(R.string.message_unselect_all) - menuItem.setIcon(R.drawable.ic_message_unselect_all) - } else { - menuItem.setTitle(R.string.message_select_all) - menuItem.setIcon(R.drawable.ic_message_select_all) - } + override fun updateData(data: List, hide: Boolean) { + if (hide) onlyUnread = null + tabAdapter.setDataItems(data, onlyUnread, onlyWithAttachments) } override fun showProgress(show: Boolean) { @@ -207,14 +146,6 @@ class MessageTabFragment : BaseFragment(R.layout.frag binding.messageTabSwipe.isRefreshing = show } - override fun showMessagesDeleted() { - showMessage(getString(R.string.message_messages_deleted)) - } - - override fun notifyParentShowNewMessage(show: Boolean) { - (parentFragment as? MessageFragment)?.onChildFragmentShowNewMessage(show) - } - override fun openMessage(message: Message) { (activity as? MainActivity)?.pushView(MessagePreviewFragment.newInstance(message)) } @@ -223,20 +154,12 @@ class MessageTabFragment : BaseFragment(R.layout.frag (parentFragment as? MessageFragment)?.onChildFragmentLoaded() } - override fun notifyParentShowActionMode(show: Boolean) { - (parentFragment as? MessageFragment)?.onChildFragmentShowActionMode(show) - } - - fun onParentLoadData(forceRefresh: Boolean) { - presenter.onParentViewLoadData(forceRefresh) - } - - fun onParentFinishActionMode() { - presenter.onParentFinishActionMode() - } - - fun onParentReselected() { - presenter.onParentReselected() + fun onParentLoadData( + forceRefresh: Boolean, + onlyUnread: Boolean? = this.onlyUnread, + onlyWithAttachments: Boolean = this.onlyWithAttachments + ) { + presenter.onParentViewLoadData(forceRefresh, onlyUnread, onlyWithAttachments) } private fun onChipChecked(chip: CompoundButton, isChecked: Boolean) { @@ -246,32 +169,8 @@ class MessageTabFragment : BaseFragment(R.layout.frag } } - override fun showActionMode(show: Boolean) { - if (show) { - actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback) - } else { - actionMode?.finish() - } - } - - override fun showRecyclerBottomPadding(show: Boolean) { - binding.messageTabRecycler.updatePadding( - bottom = if (show) requireContext().dpToPx(64f).toInt() else 0 - ) - } - - override fun showMailboxChooser(mailboxes: List) { - (activity as? MainActivity)?.showDialogFragment( - MailboxChooserDialog.newInstance( - mailboxes = mailboxes, - isMailboxRequired = false, - folder = requireArguments().getString(MESSAGE_TAB_FOLDER_ID)!!, - ) - ) - } - - override fun hideKeyboard() { - activity?.hideSoftInput() + fun onParentDeleteMessage() { + presenter.onDeleteMessage() } override fun onSaveInstanceState(outState: Bundle) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index ec92e9c2..3e5e09b4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -1,21 +1,25 @@ package io.github.wulkanowy.ui.modules.message.tab -import io.github.wulkanowy.data.* -import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.data.repositories.MessageRepository +import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import me.xdrop.fuzzywuzzy.FuzzySearch import timber.log.Timber @@ -26,6 +30,7 @@ class MessageTabPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, + private val semesterRepository: SemesterRepository, private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { @@ -35,21 +40,10 @@ class MessageTabPresenter @Inject constructor( private var lastSearchQuery = "" - private var mailboxes: List = emptyList() - private var selectedMailbox: Mailbox? = null - private var messages = emptyList() private val searchChannel = Channel() - private val messagesToDelete = mutableSetOf() - - private var onlyUnread: Boolean? = false - - private var onlyWithAttachments = false - - private var isActionMode = false - fun onAttachView(view: MessageTabView, folder: MessageFolder) { super.onAttachView(view) view.initView() @@ -60,14 +54,14 @@ class MessageTabPresenter @Inject constructor( fun onSwipeRefresh() { Timber.i("Force refreshing the $folder message") - view?.run { loadData(true) } + view?.run { onParentViewLoadData(true, onlyUnread, onlyWithAttachments) } } fun onRetry() { view?.run { showErrorView(false) showProgress(true) - loadData(true) + loadData(true, onlyUnread == true, onlyWithAttachments) } } @@ -75,204 +69,95 @@ class MessageTabPresenter @Inject constructor( view?.showErrorDetailsDialog(lastError) } - fun onParentViewLoadData(forceRefresh: Boolean) { - loadData(forceRefresh) + fun onDeleteMessage() { + view?.run { loadData(true, onlyUnread == true, onlyWithAttachments) } } - fun onParentFinishActionMode() { - view?.showActionMode(false) + fun onParentViewLoadData( + forceRefresh: Boolean, + onlyUnread: Boolean? = view?.onlyUnread, + onlyWithAttachments: Boolean = view?.onlyWithAttachments == true + ) { + loadData(forceRefresh, onlyUnread == true, onlyWithAttachments) } - fun onParentReselected() { - view?.run { - if (!isViewEmpty) { - resetListPosition() - } - } - } - - fun onDestroyActionMode() { - isActionMode = false - messagesToDelete.clear() - updateDataInView() - - view?.run { - enableSwipe(true) - notifyParentShowNewMessage(true) - notifyParentShowActionMode(false) - showRecyclerBottomPadding(true) - } - } - - fun onPrepareActionMode(): Boolean { - isActionMode = true - messagesToDelete.clear() - updateDataInView() - - view?.apply { - enableSwipe(false) - notifyParentShowNewMessage(false) - notifyParentShowActionMode(true) - showRecyclerBottomPadding(false) - hideKeyboard() - } - return true - } - - fun onActionModeSelectDelete() { - Timber.i("Delete ${messagesToDelete.size} messages)") - val messageList = messagesToDelete.toList() - - presenterScope.launch { - view?.run { - showProgress(true) - showContent(false) - showActionMode(false) - } - - runCatching { - val student = studentRepository.getCurrentStudent(true) - messageRepository.deleteMessages(student, selectedMailbox, messageList) - } - .onFailure(errorHandler::dispatch) - .onSuccess { view?.showMessagesDeleted() } - } - } - - fun onActionModeSelectCheckAll() { - val messagesToSelect = getFilteredData() - val isAllSelected = messagesToDelete.containsAll(messagesToSelect) - - if (isAllSelected) { - messagesToDelete.clear() - view?.showActionMode(false) - } else { - messagesToDelete.addAll(messagesToSelect) - updateDataInView() - } - - view?.run { - updateSelectAllMenu(!isAllSelected) - updateActionModeTitle(messagesToDelete.size) - } - } - - fun onMessageItemLongSelected(messageItem: MessageTabDataItem.MessageItem) { - if (!isActionMode) { - view?.showActionMode(true) - - messagesToDelete.add(messageItem.message) - - view?.updateActionModeTitle(messagesToDelete.size) - updateDataInView() - } - } - - fun onMessageItemSelected(messageItem: MessageTabDataItem.MessageItem, position: Int) { - Timber.i("Select message ${messageItem.message.messageGlobalKey} item (position: $position)") - - if (!isActionMode) { - view?.run { - showActionMode(false) - openMessage(messageItem.message) - } - } else { - if (!messageItem.isSelected) { - messagesToDelete.add(messageItem.message) - } else { - messagesToDelete.remove(messageItem.message) - } - - if (messagesToDelete.isEmpty()) { - view?.showActionMode(false) - } - - val filteredData = getFilteredData() - - view?.run { - updateActionModeTitle(messagesToDelete.size) - updateSelectAllMenu(messagesToDelete.containsAll(filteredData)) - } - updateDataInView() - } + fun onMessageItemSelected(message: Message, position: Int) { + Timber.i("Select message ${message.id} item (position: $position)") + view?.openMessage(message) } fun onUnreadFilterSelected(isChecked: Boolean) { view?.run { onlyUnread = isChecked - loadData(false) + onParentViewLoadData(false, onlyUnread, onlyWithAttachments) } } fun onAttachmentsFilterSelected(isChecked: Boolean) { view?.run { onlyWithAttachments = isChecked - loadData(false) + onParentViewLoadData(false, onlyUnread, onlyWithAttachments) } } - fun onMailboxFilterSelected() { - view?.showMailboxChooser(mailboxes) - } - - fun onMailboxSelected(mailbox: Mailbox?) { - selectedMailbox = mailbox - loadData(false) - } - - private fun loadData(forceRefresh: Boolean) { + private fun loadData( + forceRefresh: Boolean, + onlyUnread: Boolean, + onlyWithAttachments: Boolean + ) { Timber.i("Loading $folder message data started") - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() - - if (selectedMailbox == null && mailboxes.isEmpty()) { - selectedMailbox = messageRepository.getMailboxByStudent(student) - mailboxes = messageRepository.getMailboxes(student, forceRefresh).toFirstResult() - .dataOrNull.orEmpty() - } - - messageRepository.getMessages(student, selectedMailbox, folder, forceRefresh) - } - .logResourceStatus("load $folder message") - .onResourceData { - messages = it - - val filteredData = getFilteredData() - - view?.run { - enableSwipe(true) - showErrorView(false) - showProgress(false) - showContent(true) - showEmpty(filteredData.isEmpty()) + val semester = semesterRepository.getCurrentSemester(student) + messageRepository.getMessages(student, semester, folder, forceRefresh) + }.onEach { + when (it.status) { + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + messages = it.data + val filteredData = getFilteredData( + lastSearchQuery, + onlyUnread, + onlyWithAttachments + ) + val newItems = listOf(MessageTabDataItem.Header) + filteredData.map { + MessageTabDataItem.MessageItem(it) + } + updateData(newItems, folder.id == MessageFolder.SENT.id) + notifyParentDataLoaded() + } + } } - - updateDataInView() - } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "messages", - "items" to it.size, - "folder" to folder.name - ) - } - .onResourceNotLoading { - view?.run { - showRefresh(false) - showProgress(false) - enableSwipe(true) - notifyParentDataLoaded() + Status.SUCCESS -> { + Timber.i("Loading $folder message result: Success") + messages = it.data!! + updateData(getFilteredData(lastSearchQuery, onlyUnread, onlyWithAttachments)) + analytics.logEvent( + "load_data", + "type" to "messages", + "items" to it.data.size, + "folder" to folder.name + ) + } + Status.ERROR -> { + Timber.i("Loading $folder message result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceError(errorHandler::dispatch) - .catch { - errorHandler.dispatch(it) - view?.notifyParentDataLoaded() + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded() } - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -282,106 +167,82 @@ class MessageTabPresenter @Inject constructor( setErrorDetails(message) showErrorView(true) showEmpty(false) - showProgress(false) } else showError(message, error) } } fun onSearchQueryTextChange(query: String) { - presenterScope.launch { + launch { searchChannel.send(query) } } @OptIn(FlowPreview::class) private fun initializeSearchStream() { - presenterScope.launch { + launch { searchChannel.consumeAsFlow() .debounce(250) .map { query -> lastSearchQuery = query - - getFilteredData() + val isOnlyUnread = view?.onlyUnread == true + val isOnlyWithAttachments = view?.onlyWithAttachments == true + getFilteredData(query, isOnlyUnread, isOnlyWithAttachments) } .catch { Timber.e(it) } .collect { Timber.d("Applying filter. Full list: ${messages.size}, filtered: ${it.size}") - - view?.run { - showEmpty(it.isEmpty()) - showContent(true) - showErrorView(false) - } - - updateDataInView() + updateData(it) view?.resetListPosition() } } } - private fun getFilteredData(): List { - if (lastSearchQuery.trim().isEmpty()) { + private fun getFilteredData( + query: String, + onlyUnread: Boolean = false, + onlyWithAttachments: Boolean = false + ): List { + if (query.trim().isEmpty()) { val sortedMessages = messages.sortedByDescending { it.date } return when { - (onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments } - (onlyUnread == true) -> sortedMessages.filter { it.unread == onlyUnread } + onlyUnread && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments } + onlyUnread -> sortedMessages.filter { it.unread == onlyUnread } onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments } else -> sortedMessages } } else { val sortedMessages = messages - .map { it to calculateMatchRatio(it, lastSearchQuery) } + .map { it to calculateMatchRatio(it, query) } .sortedWith(compareBy> { -it.second }.thenByDescending { it.first.date }) .filter { it.second > 6000 } .map { it.first } return when { - (onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments } - (onlyUnread == true) -> sortedMessages.filter { it.unread == onlyUnread } + onlyUnread && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments } + onlyUnread -> sortedMessages.filter { it.unread == onlyUnread } onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments } else -> sortedMessages } } } - private fun updateDataInView() { - val data = getFilteredData() - - val list = buildList { - add( - MessageTabDataItem.FilterHeader( - onlyUnread = onlyUnread.takeIf { folder != MessageFolder.SENT }, - onlyWithAttachments = onlyWithAttachments, - isEnabled = !isActionMode, - selectedMailbox = selectedMailbox?.let { - buildString { - if (it.studentName.isNotBlank() && it.studentName != it.userName) { - append(it.studentName) - append(" - ") - } - append(it.userName) - } - }, - ) - ) - - addAll(data.map { message -> - MessageTabDataItem.MessageItem( - message = message, - isSelected = messagesToDelete.any { it.messageGlobalKey == message.messageGlobalKey }, - isActionMode = isActionMode - ) - }) + private fun updateData(data: List) { + view?.run { + showEmpty(data.isEmpty()) + showContent(true) + showErrorView(false) + val newItems = + listOf(MessageTabDataItem.Header) + data.map { MessageTabDataItem.MessageItem(it) } + updateData(newItems, folder.id == MessageFolder.SENT.id) } - - view?.updateData(list) } private fun calculateMatchRatio(message: Message, query: String): Int { val subjectRatio = FuzzySearch.tokenSortPartialRatio(query.lowercase(), message.subject) - val correspondentsRatio = FuzzySearch.tokenSortPartialRatio( + val senderOrRecipientRatio = FuzzySearch.tokenSortPartialRatio( query.lowercase(), - message.correspondents + if (message.sender.isNotEmpty()) message.sender.lowercase() + else message.recipient.lowercase() ) val dateRatio = listOf( @@ -397,7 +258,7 @@ class MessageTabPresenter @Inject constructor( return (subjectRatio.toDouble().pow(2) - + correspondentsRatio.toDouble().pow(2) + + senderOrRecipientRatio.toDouble().pow(2) + dateRatio.toDouble().pow(2) * 2 ).toInt() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt index 6ece6621..a856da3b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.message.tab -import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.ui.base.BaseView @@ -8,15 +7,15 @@ interface MessageTabView : BaseView { val isViewEmpty: Boolean + var onlyUnread: Boolean? + + var onlyWithAttachments: Boolean + fun initView() fun resetListPosition() - fun updateData(data: List) - - fun updateActionModeTitle(selectedMessagesSize: Int) - - fun updateSelectAllMenu(isAllSelected: Boolean) + fun updateData(data: List, hide: Boolean) fun showProgress(show: Boolean) @@ -26,12 +25,8 @@ interface MessageTabView : BaseView { fun showEmpty(show: Boolean) - fun showMessagesDeleted() - fun showErrorView(show: Boolean) - fun notifyParentShowNewMessage(show: Boolean) - fun setErrorDetails(message: String) fun showRefresh(show: Boolean) @@ -39,14 +34,4 @@ interface MessageTabView : BaseView { fun openMessage(message: Message) fun notifyParentDataLoaded() - - fun notifyParentShowActionMode(show: Boolean) - - fun hideKeyboard() - - fun showActionMode(show: Boolean) - - fun showRecyclerBottomPadding(show: Boolean) - - fun showMailboxChooser(mailboxes: List) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt index 1afe773c..f8e367c5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt @@ -22,7 +22,7 @@ import javax.inject.Inject @AndroidEntryPoint class MobileDeviceFragment : BaseFragment(R.layout.fragment_mobile_device), MobileDeviceView, - MainView.TitledView, MainView.MainChildView { + MainView.TitledView { @Inject lateinit var presenter: MobileDevicePresenter @@ -135,14 +135,6 @@ class MobileDeviceFragment : (activity as? MainActivity)?.showDialogFragment(MobileDeviceTokenDialog.newInstance()) } - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onFragmentReselected() - } - - override fun resetView() { - binding.mobileDevicesRecycler.smoothScrollToPosition(0) - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt index 56785dbf..9591867d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.mobiledevice -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.MobileDevice import io.github.wulkanowy.data.repositories.MobileDeviceRepository import io.github.wulkanowy.data.repositories.SemesterRepository @@ -8,6 +8,11 @@ import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -48,39 +53,49 @@ class MobileDevicePresenter @Inject constructor( private fun loadData(forceRefresh: Boolean = false) { Timber.i("Loading mobile devices data started") - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) mobileDeviceRepository.getDevices(student, semester, forceRefresh) - } - .logResourceStatus("load mobile devices data") - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) - updateData(it) + }.onEach { + when (it.status) { + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading mobile devices result: Success") + view?.run { + updateData(it.data!!) + showContent(it.data.isNotEmpty()) + showEmpty(it.data.isEmpty()) + showErrorView(false) + } + analytics.logEvent( + "load_data", + "type" to "devices", + "items" to it.data!!.size + ) + } + Status.ERROR -> { + Timber.i("Loading mobile devices result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "devices", - "items" to it.size - ) + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showProgress(false) - showRefresh(false) - } - } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -114,25 +129,25 @@ class MobileDevicePresenter @Inject constructor( } fun onUnregisterConfirmed(device: MobileDevice) { - resourceFlow { + flowWithResource { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) mobileDeviceRepository.unregisterDevice(student, semester, device) - } - .logResourceStatus("unregister device") - .onResourceSuccess { - view?.run { - showProgress(false) - enableSwipe(true) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Unregister device started") + Status.SUCCESS -> { + Timber.i("Unregister device result: Success") + view?.run { + showProgress(false) + enableSwipe(true) + } + } + Status.ERROR -> { + Timber.i("Unregister device result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceError(errorHandler::dispatch) - .launch("unregister") - } - - fun onFragmentReselected() { - if (view?.isViewEmpty == false) { - view?.resetView() - } + }.launchIn(this) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt index 973cb123..b94646a7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt @@ -32,6 +32,4 @@ interface MobileDeviceView : BaseView { fun setErrorDetails(message: String) fun showTokenDialog() - - fun resetView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt index 2cc2a2aa..eb420a6a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt @@ -1,17 +1,17 @@ package io.github.wulkanowy.ui.modules.mobiledevice.token -import android.app.Dialog import android.content.ClipData import android.content.ClipboardManager import android.graphics.BitmapFactory import android.os.Bundle import android.util.Base64 +import android.view.LayoutInflater import android.view.View import android.view.View.GONE import android.view.View.VISIBLE +import android.view.ViewGroup import android.widget.Toast import androidx.core.content.getSystemService -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.pojos.MobileDeviceToken @@ -31,14 +31,17 @@ class MobileDeviceTokenDialog : BaseDialogFragment(), fun newInstance() = MobileDeviceTokenDialog() } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView( - DialogMobileDeviceBinding.inflate(layoutInflater).apply { binding = this }.root - ) - .create() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogMobileDeviceBinding.inflate(inflater).apply { binding = this }.root + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) presenter.onAttachView(this) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt index 875b73ad..5e7110ee 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt @@ -1,12 +1,15 @@ package io.github.wulkanowy.ui.modules.mobiledevice.token -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.MobileDeviceRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -26,29 +29,29 @@ class MobileDeviceTokenPresenter @Inject constructor( } private fun loadData() { - resourceFlow { + flowWithResource { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) mobileDeviceRepository.getToken(student, semester) - } - .logResourceStatus("load mobile device registration") - .onResourceData { - view?.run { - updateData(it) - showContent() + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Mobile device registration data started") + Status.SUCCESS -> { + Timber.i("Mobile device registration result: Success") + view?.run { + updateData(it.data!!) + showContent() + } + analytics.logEvent("device_register", "symbol" to it.data!!.token.substring(0, 3)) + } + Status.ERROR -> { + Timber.i("Mobile device registration result: An exception occurred") + view?.closeDialog() + errorHandler.dispatch(it.error!!) } } - .onResourceSuccess { - analytics.logEvent( - "device_register", - "symbol" to it.token.substring(0, 3) - ) - } - .onResourceNotLoading { view?.hideLoading() } - .onResourceError { - view?.closeDialog() - errorHandler.dispatch(it) - } - .launch() + }.afterLoading { + view?.hideLoading() + }.launch() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt index 697eab33..70587b0c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.more +import android.graphics.drawable.Drawable import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView @@ -8,9 +9,9 @@ import javax.inject.Inject class MoreAdapter @Inject constructor() : RecyclerView.Adapter() { - var items = emptyList() + var items = emptyList>() - var onClickListener: (moreItem: MoreItem) -> Unit = {} + var onClickListener: (name: String) -> Unit = {} override fun getItemCount() = items.size @@ -19,14 +20,13 @@ class MoreAdapter @Inject constructor() : RecyclerView.Adapter(R.layout.fragment_more), override val titleStringId: Int get() = R.string.more_title + override val messagesRes: Pair? + get() = context?.run { getString(R.string.message_title) to getCompatDrawable(R.drawable.ic_more_messages) } + + override val homeworkRes: Pair? + get() = context?.run { getString(R.string.homework_title) to getCompatDrawable(R.drawable.ic_more_homework) } + + override val noteRes: Pair? + get() = context?.run { getString(R.string.note_title) to getCompatDrawable(R.drawable.ic_more_note) } + + override val conferencesRes: Pair? + get() = context?.run { getString(R.string.conferences_title) to getCompatDrawable(R.drawable.ic_more_conferences) } + + override val schoolAnnouncementRes: Pair? + get() = context?.run { getString(R.string.school_announcement_title) to getCompatDrawable(R.drawable.ic_all_about) } + + override val schoolAndTeachersRes: Pair? + get() = context?.run { getString(R.string.schoolandteachers_title) to getCompatDrawable((R.drawable.ic_more_schoolandteachers)) } + + override val mobileDevicesRes: Pair? + get() = context?.run { getString(R.string.mobile_devices_title) to getCompatDrawable(R.drawable.ic_more_mobile_devices) } + + override val settingsRes: Pair? + get() = context?.run { getString(R.string.settings_title) to getCompatDrawable(R.drawable.ic_more_settings) } + + override val examRes: Pair? + get() = context?.run { getString(R.string.exam_title) to getCompatDrawable(R.drawable.ic_main_exam) } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentMoreBinding.bind(view) @@ -49,24 +85,51 @@ class MoreFragment : BaseFragment(R.layout.fragment_more), if (::presenter.isInitialized) presenter.onViewReselected() } - override fun onFragmentChanged() { - (parentFragmentManager.fragments.find { it is MessageFragment } as MessageFragment?) - ?.onFragmentChanged() - } - - override fun updateData(data: List) { + override fun updateData(data: List>) { with(moreAdapter) { items = data notifyDataSetChanged() } } - override fun popView(depth: Int) { - (activity as? MainActivity)?.popView(depth) + override fun openMessagesView() { + (activity as? MainActivity)?.pushView(MessageFragment.newInstance()) } - override fun openView(destination: Destination) { - (activity as? MainActivity)?.pushView(destination.destinationFragment) + override fun openHomeworkView() { + (activity as? MainActivity)?.pushView(HomeworkFragment.newInstance()) + } + + override fun openNoteView() { + (activity as? MainActivity)?.pushView(NoteFragment.newInstance()) + } + + override fun openSchoolAnnouncementView() { + (activity as? MainActivity)?.pushView(SchoolAnnouncementFragment.newInstance()) + } + + override fun openConferencesView() { + (activity as? MainActivity)?.pushView(ConferenceFragment.newInstance()) + } + + override fun openSchoolAndTeachersView() { + (activity as? MainActivity)?.pushView(SchoolAndTeachersFragment.newInstance()) + } + + override fun openMobileDevicesView() { + (activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance()) + } + + override fun openSettingsView() { + (activity as? MainActivity)?.pushView(SettingsFragment.newInstance()) + } + + override fun openExamView() { + (activity as? MainActivity)?.pushView(ExamFragment.newInstance()) + } + + override fun popView(depth: Int) { + (activity as? MainActivity)?.popView(depth) } override fun onDestroyView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt deleted file mode 100644 index d544f041..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.wulkanowy.ui.modules.more - -import io.github.wulkanowy.ui.modules.Destination - -data class MoreItem( - - val icon: Int, - - val title: Int, - - val destination: Destination -) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt index 0ebaf4c7..a2b7f204 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt @@ -1,24 +1,16 @@ package io.github.wulkanowy.ui.modules.more -import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.ui.modules.Destination import timber.log.Timber import javax.inject.Inject class MorePresenter @Inject constructor( errorHandler: ErrorHandler, - studentRepository: StudentRepository, - preferencesRepository: PreferencesRepository + studentRepository: StudentRepository ) : BasePresenter(errorHandler, studentRepository) { - private val moreAppMenuItem = preferencesRepository.appMenuItemOrder - .sortedBy { it.order } - .drop(4) - override fun onAttachView(view: MoreView) { super.onAttachView(view) view.initView() @@ -26,10 +18,21 @@ class MorePresenter @Inject constructor( loadData() } - fun onItemSelected(moreItem: MoreItem) { - Timber.i("Select more item \"${moreItem.destination.destinationType}\"") - - view?.openView(moreItem.destination) + fun onItemSelected(title: String) { + Timber.i("Select more item \"${title}\"") + view?.run { + when (title) { + messagesRes?.first -> openMessagesView() + examRes?.first -> openExamView() + homeworkRes?.first -> openHomeworkView() + noteRes?.first -> openNoteView() + conferencesRes?.first -> openConferencesView() + schoolAnnouncementRes?.first -> openSchoolAnnouncementView() + schoolAndTeachersRes?.first -> openSchoolAndTeachersView() + mobileDevicesRes?.first -> openMobileDevicesView() + settingsRes?.first -> openSettingsView() + } + } } fun onViewReselected() { @@ -39,21 +42,18 @@ class MorePresenter @Inject constructor( private fun loadData() { Timber.i("Load items for more view") - val moreItems = moreAppMenuItem.map { - MoreItem( - icon = it.icon, - title = it.title, - destination = it.destinationType.defaultDestination - ) + view?.run { + updateData(listOfNotNull( + messagesRes, + examRes, + homeworkRes, + noteRes, + conferencesRes, + schoolAnnouncementRes, + schoolAndTeachersRes, + mobileDevicesRes, + settingsRes + )) } - .plus( - MoreItem( - icon = R.drawable.ic_more_settings, - title = R.string.settings_title, - destination = Destination.Settings - ) - ) - - view?.updateData(moreItems) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt index fbca97ed..c4a07bdc 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt @@ -1,15 +1,49 @@ package io.github.wulkanowy.ui.modules.more +import android.graphics.drawable.Drawable import io.github.wulkanowy.ui.base.BaseView -import io.github.wulkanowy.ui.modules.Destination interface MoreView : BaseView { + val messagesRes: Pair? + + val homeworkRes: Pair? + + val noteRes: Pair? + + val conferencesRes: Pair? + + val schoolAnnouncementRes: Pair? + + val schoolAndTeachersRes: Pair? + + val mobileDevicesRes: Pair? + + val settingsRes: Pair? + + val examRes: Pair? + fun initView() - fun updateData(data: List) + fun updateData(data: List>) + + fun openSettingsView() fun popView(depth: Int) - fun openView(destination: Destination) + fun openMessagesView() + + fun openHomeworkView() + + fun openNoteView() + + fun openSchoolAnnouncementView() + + fun openConferencesView() + + fun openSchoolAndTeachersView() + + fun openMobileDevicesView() + + fun openExamView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt index 0592e924..5811456b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt @@ -1,24 +1,23 @@ package io.github.wulkanowy.ui.modules.note import android.annotation.SuppressLint -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.core.content.ContextCompat -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.databinding.DialogNoteBinding import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory -import io.github.wulkanowy.ui.base.BaseDialogFragment import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.serializable +import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.toFormattedString -@AndroidEntryPoint -class NoteDialog : BaseDialogFragment() { +class NoteDialog : DialogFragment() { + + private var binding: DialogNoteBinding by lifecycleAwareVariable() private lateinit var note: Note @@ -26,21 +25,24 @@ class NoteDialog : BaseDialogFragment() { private const val ARGUMENT_KEY = "Item" - fun newInstance(note: Note) = NoteDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to note) + fun newInstance(exam: Note) = NoteDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - note = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + note = getSerializable(ARGUMENT_KEY) as Note + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogNoteBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogNoteBinding.inflate(inflater).apply { binding = this }.root @SuppressLint("SetTextI18n") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt index cb54b384..dd622344 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt @@ -18,7 +18,7 @@ import javax.inject.Inject @AndroidEntryPoint class NoteFragment : BaseFragment(R.layout.fragment_note), NoteView, - MainView.TitledView, MainView.MainChildView { + MainView.TitledView { @Inject lateinit var presenter: NotePresenter @@ -112,14 +112,6 @@ class NoteFragment : BaseFragment(R.layout.fragment_note), binding.noteSwipe.isRefreshing = show } - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onFragmentReselected() - } - - override fun resetView() { - binding.noteRecycler.smoothScrollToPosition(0) - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt index 62ad347f..c441231e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.note -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.repositories.NoteRepository import io.github.wulkanowy.data.repositories.SemesterRepository @@ -8,6 +8,10 @@ import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResource +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -48,40 +52,51 @@ class NotePresenter @Inject constructor( } private fun loadData(forceRefresh: Boolean = false) { - flatResourceFlow { + Timber.i("Loading note data started") + + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) noteRepository.getNotes(student, semester, forceRefresh) - } - .logResourceStatus("load note data") - .mapResourceData { it.sortedByDescending { note -> note.date } } - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) - updateData(it) + }.onEach { + when (it.status) { + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data.sortedByDescending { item -> item.date }) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading note result: Success") + view?.apply { + updateData(it.data!!.sortedByDescending { item -> item.date }) + showEmpty(it.data.isEmpty()) + showErrorView(false) + showContent(it.data.isNotEmpty()) + } + analytics.logEvent( + "load_data", + "type" to "note", + "items" to it.data!!.size + ) + } + Status.ERROR -> { + Timber.i("Loading note result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "note", - "items" to it.size - ) + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showProgress(false) - showRefresh(false) - } - } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -108,23 +123,15 @@ class NotePresenter @Inject constructor( } private fun updateNote(note: Note) { - resourceFlow { noteRepository.updateNote(note) } - .onEach { - when (it) { - is Resource.Loading -> Timber.i("Attempt to update note ${note.id}") - is Resource.Success -> Timber.i("Update note result: Success") - is Resource.Error -> { - Timber.i("Update note result: An exception occurred") - errorHandler.dispatch(it.error) - } + flowWithResource { noteRepository.updateNote(note) }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Attempt to update note ${note.id}") + Status.SUCCESS -> Timber.i("Update note result: Success") + Status.ERROR -> { + Timber.i("Update note result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .launch("update_note") - } - - fun onFragmentReselected() { - if (view?.isViewEmpty == false) { - view?.resetView() - } + }.launchIn(this) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt index b813b315..9fc0be94 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt @@ -30,6 +30,4 @@ interface NoteView : BaseView { fun showRefresh(show: Boolean) fun showNoteDialog(note: Note) - - fun resetView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notifications/NotificationsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notifications/NotificationsFragment.kt deleted file mode 100644 index 0763d4fa..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/notifications/NotificationsFragment.kt +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.wulkanowy.ui.modules.notifications - -import android.os.Bundle -import android.view.View -import androidx.activity.result.contract.ActivityResultContracts.RequestPermission -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.databinding.FragmentNotificationsBinding -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.utils.openNotificationSettings - -@AndroidEntryPoint -class NotificationsFragment : - BaseFragment(R.layout.fragment_notifications) { - - private val permission = "android.permission.POST_NOTIFICATIONS" - - private val requestPermissionLauncher = registerForActivityResult(RequestPermission()) { - if (it) { - navigateToFinish() - } else showSettingsDialog() - } - - companion object { - fun newInstance() = NotificationsFragment() - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding = FragmentNotificationsBinding.bind(view) - initView() - } - - private fun initView() { - with(binding) { - notificationsSkip.setOnClickListener { navigateToFinish() } - notificationsEnable.setOnClickListener { requestPermission() } - } - } - - private fun showSettingsDialog() { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.notifications_header_title) - .setMessage(R.string.notifications_header_description) - .setNegativeButton(R.string.notifications_skip) { dialog, _ -> - dialog.dismiss() - navigateToFinish() - } - .setPositiveButton(R.string.pref_notification_go_to_settings) { _, _ -> - requireActivity().openNotificationSettings() - } - .show() - } - - private fun requestPermission() { - requestPermissionLauncher.launch(permission) - } - - private fun navigateToFinish() { - (requireActivity() as LoginActivity).navigateToFinish() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterAdapter.kt deleted file mode 100644 index 92c54f45..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterAdapter.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.wulkanowy.ui.modules.notificationscenter - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import io.github.wulkanowy.data.db.entities.Notification -import io.github.wulkanowy.databinding.ItemNotificationsCenterBinding -import io.github.wulkanowy.utils.toFormattedString -import javax.inject.Inject - -class NotificationsCenterAdapter @Inject constructor() : - ListAdapter(DiffUtilCallback()) { - - var onItemClickListener: (Notification) -> Unit = {} - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( - ItemNotificationsCenterBinding.inflate(LayoutInflater.from(parent.context), parent, false) - ) - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val item = getItem(position) - - with(holder.binding) { - notificationsCenterItemTitle.text = item.title - notificationsCenterItemContent.text = item.content - notificationsCenterItemDate.text = item.date.toFormattedString("HH:mm, d MMM") - notificationsCenterItemIcon.setImageResource(item.type.icon) - - root.setOnClickListener { onItemClickListener(item) } - } - } - - class ViewHolder(val binding: ItemNotificationsCenterBinding) : - RecyclerView.ViewHolder(binding.root) - - private class DiffUtilCallback : DiffUtil.ItemCallback() { - - override fun areContentsTheSame(oldItem: Notification, newItem: Notification) = - oldItem == newItem - - override fun areItemsTheSame(oldItem: Notification, newItem: Notification) = - oldItem.id == newItem.id - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt deleted file mode 100644 index ca71910a..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt +++ /dev/null @@ -1,83 +0,0 @@ -package io.github.wulkanowy.ui.modules.notificationscenter - -import android.os.Bundle -import android.view.View -import androidx.core.view.isVisible -import androidx.recyclerview.widget.LinearLayoutManager -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Notification -import io.github.wulkanowy.databinding.FragmentNotificationsCenterBinding -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import javax.inject.Inject - -@AndroidEntryPoint -class NotificationsCenterFragment : - BaseFragment(R.layout.fragment_notifications_center), - NotificationsCenterView, MainView.TitledView { - - @Inject - lateinit var presenter: NotificationsCenterPresenter - - @Inject - lateinit var notificationsCenterAdapter: NotificationsCenterAdapter - - companion object { - - fun newInstance() = NotificationsCenterFragment() - } - - override val titleStringId: Int - get() = R.string.notifications_center_title - - override val isViewEmpty: Boolean - get() = notificationsCenterAdapter.itemCount == 0 - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding = FragmentNotificationsCenterBinding.bind(view) - presenter.onAttachView(this) - } - - override fun initView() { - notificationsCenterAdapter.onItemClickListener = { notification -> - (requireActivity() as MainActivity).pushView(notification.destination.destinationFragment) - } - - with(binding.notificationsCenterRecycler) { - layoutManager = LinearLayoutManager(context) - adapter = notificationsCenterAdapter - } - } - - override fun updateData(data: List) { - notificationsCenterAdapter.submitList(data) - } - - override fun showEmpty(show: Boolean) { - binding.notificationsCenterEmpty.isVisible = show - } - - override fun showProgress(show: Boolean) { - binding.notificationsCenterProgress.isVisible = show - } - - override fun showContent(show: Boolean) { - binding.notificationsCenterRecycler.isVisible = show - } - - override fun showErrorView(show: Boolean) { - binding.notificationCenterError.isVisible = show - } - - override fun setErrorDetails(message: String) { - binding.notificationCenterErrorMessage.text = message - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterPresenter.kt deleted file mode 100644 index de42e567..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterPresenter.kt +++ /dev/null @@ -1,83 +0,0 @@ -package io.github.wulkanowy.ui.modules.notificationscenter - -import io.github.wulkanowy.data.repositories.NotificationRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.emitAll -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import timber.log.Timber -import javax.inject.Inject - -class NotificationsCenterPresenter @Inject constructor( - private val notificationRepository: NotificationRepository, - errorHandler: ErrorHandler, - studentRepository: StudentRepository -) : BasePresenter(errorHandler, studentRepository) { - - private lateinit var lastError: Throwable - - override fun onAttachView(view: NotificationsCenterView) { - super.onAttachView(view) - view.initView() - Timber.i("Notifications centre view was initialized") - errorHandler.showErrorMessage = ::showErrorViewOnError - loadData() - } - - fun onRetry() { - view?.run { - showErrorView(false) - showProgress(true) - } - loadData() - } - - fun onDetailsClick() { - view?.showErrorDetailsDialog(lastError) - } - - private fun loadData() { - Timber.i("Loading notifications data started") - - flow { - val studentId = studentRepository.getCurrentStudent(false).id - emitAll(notificationRepository.getNotifications(studentId)) - } - .map { notificationList -> notificationList.sortedByDescending { it.date } } - .catch { Timber.i("Loading notifications result: An exception occurred: `$it`") } - .onEach { - Timber.i("Loading notifications result: Success") - - if (it.isEmpty()) { - view?.run { - showContent(false) - showProgress(false) - showEmpty(true) - } - } else { - view?.run { - showContent(true) - showProgress(false) - showEmpty(false) - updateData(it) - } - } - } - .launch() - } - - private fun showErrorViewOnError(message: String, error: Throwable) { - view?.run { - if (isViewEmpty) { - lastError = error - setErrorDetails(message) - showErrorView(true) - showEmpty(false) - } else showError(message, error) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterView.kt deleted file mode 100644 index 1bfbe75e..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterView.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.ui.modules.notificationscenter - -import io.github.wulkanowy.data.db.entities.Notification -import io.github.wulkanowy.ui.base.BaseView - -interface NotificationsCenterView : BaseView { - - val isViewEmpty: Boolean - - fun initView() - - fun updateData(data: List) - - fun showProgress(show: Boolean) - - fun showEmpty(show: Boolean) - - fun showContent(show: Boolean) - - fun showErrorView(show: Boolean) - - fun setErrorDetails(message: String) -} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersFragment.kt index f4fa8e01..c1c56961 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersFragment.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.View import android.view.View.INVISIBLE import android.view.View.VISIBLE -import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.databinding.FragmentSchoolandteachersBinding @@ -25,13 +24,7 @@ class SchoolAndTeachersFragment : @Inject lateinit var presenter: SchoolAndTeachersPresenter - private val pagerAdapter by lazy { - BaseFragmentPagerAdapter( - fragmentManager = childFragmentManager, - pagesCount = 2, - lifecycle = lifecycle, - ) - } + private val pagerAdapter by lazy { BaseFragmentPagerAdapter(childFragmentManager) } companion object { fun newInstance() = SchoolAndTeachersFragment() @@ -48,36 +41,24 @@ class SchoolAndTeachersFragment : } override fun initView() { + with(pagerAdapter) { + containerId = binding.schoolandteachersViewPager.id + addFragmentsWithTitle(mapOf( + SchoolFragment.newInstance() to getString(R.string.school_title), + TeacherFragment.newInstance() to getString(R.string.teachers_title) + )) + } + with(binding.schoolandteachersViewPager) { adapter = pagerAdapter offscreenPageLimit = 2 setOnSelectPageListener(presenter::onPageSelected) } - with(pagerAdapter) { - containerId = binding.schoolandteachersViewPager.id - titleFactory = { - when (it) { - 0 -> getString(R.string.school_title) - 1 -> getString(R.string.teachers_title) - else -> throw IllegalStateException() - } - } - itemFactory = { - when (it) { - 0 -> SchoolFragment.newInstance() - 1 -> TeacherFragment.newInstance() - else -> throw IllegalStateException() - } - } - TabLayoutMediator( - binding.schoolandteachersTabLayout, - binding.schoolandteachersViewPager, - this - ).attach() + with(binding.schoolandteachersTabLayout) { + setupWithViewPager(binding.schoolandteachersViewPager) + setElevationCompat(context.dpToPx(4f)) } - - binding.schoolandteachersTabLayout.elevation = requireContext().dpToPx(4f) } override fun showContent(show: Boolean) { @@ -96,8 +77,7 @@ class SchoolAndTeachersFragment : } override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) { - (pagerAdapter.getFragmentInstance(index) as? SchoolAndTeachersChildView) - ?.onParentLoadData(forceRefresh) + (pagerAdapter.getFragmentInstance(index) as? SchoolAndTeachersChildView)?.onParentLoadData(forceRefresh) } override fun onDestroyView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersPresenter.kt index 43823d6b..915cc421 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersPresenter.kt @@ -15,7 +15,8 @@ class SchoolAndTeachersPresenter @Inject constructor( override fun onAttachView(view: SchoolAndTeachersView) { super.onAttachView(view) - presenterScope.launch { + launch { + delay(150) view.initView() Timber.i("Message view was initialized") loadData() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt index 262398b8..202d4e5d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt @@ -1,13 +1,15 @@ package io.github.wulkanowy.ui.modules.schoolandteachers.school -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.SchoolRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper -import kotlinx.coroutines.flow.catch +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -62,48 +64,43 @@ class SchoolPresenter @Inject constructor( } private fun loadData(forceRefresh: Boolean = false) { - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) schoolRepository.getSchoolInfo(student, semester, forceRefresh) - } - .logResourceStatus("load school info") - .onResourceData { - if (it != null) { + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading school info started") + Status.SUCCESS -> if (it.data != null) { + Timber.i("Loading teachers result: Success") view?.run { - address = it.address.ifBlank { null } - contact = it.contact.ifBlank { null } - updateData(it) + address = it.data.address.ifBlank { null } + contact = it.data.contact.ifBlank { null } + updateData(it.data) showContent(true) showEmpty(false) showErrorView(false) } + analytics.logEvent("load_item", "type" to "school") } else view?.run { Timber.i("Loading school result: No school info found") showContent(!isViewEmpty) showEmpty(isViewEmpty) showErrorView(false) } - } - .onResourceSuccess { - if (it != null) { - analytics.logEvent("load_item", "type" to "school") + Status.ERROR -> { + Timber.i("Loading school result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceNotLoading { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - notifyParentDataLoaded() - } + }.afterLoading { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded() } - .onResourceError(errorHandler::dispatch) - .catch { - errorHandler.dispatch(it) - view?.notifyParentDataLoaded() - } - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -114,7 +111,6 @@ class SchoolPresenter @Inject constructor( showErrorView(true) showEmpty(false) showContent(false) - showProgress(false) } else showError(message, error) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt index e2af05c9..c83cfe76 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt @@ -1,13 +1,15 @@ package io.github.wulkanowy.ui.modules.schoolandteachers.teacher -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.TeacherRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper -import kotlinx.coroutines.flow.catch +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -50,41 +52,40 @@ class TeacherPresenter @Inject constructor( } private fun loadData(forceRefresh: Boolean = false) { - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) teacherRepository.getTeachers(student, semester, forceRefresh) - } - .logResourceStatus("load teachers data") - .onResourceData { - view?.run { - updateData(it.filter { item -> item.name.isNotBlank() }) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) - showErrorView(false) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading teachers data started") + Status.SUCCESS -> { + Timber.i("Loading teachers result: Success") + view?.run { + updateData(it.data!!.filter { item -> item.name.isNotBlank() }) + showContent(it.data.isNotEmpty()) + showEmpty(it.data.isEmpty()) + showErrorView(false) + } + analytics.logEvent( + "load_data", + "type" to "teachers", + "items" to it.data!!.size + ) + } + Status.ERROR -> { + Timber.i("Loading teachers result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "teachers", - "items" to it.size - ) + }.afterLoading { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded() } - .onResourceNotLoading { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - notifyParentDataLoaded() - } - } - .onResourceError(errorHandler::dispatch) - .catch { - errorHandler.dispatch(it) - view?.notifyParentDataLoaded() - } - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -94,7 +95,6 @@ class TeacherPresenter @Inject constructor( setErrorDetails(message) showErrorView(true) showEmpty(false) - showProgress(false) } else showError(message, error) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt index 46999599..e7c20267 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt @@ -2,10 +2,10 @@ package io.github.wulkanowy.ui.modules.schoolannouncement import android.view.LayoutInflater import android.view.ViewGroup +import androidx.core.text.HtmlCompat import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.data.db.entities.SchoolAnnouncement import io.github.wulkanowy.databinding.ItemSchoolAnnouncementBinding -import io.github.wulkanowy.utils.parseUonetHtml import io.github.wulkanowy.utils.toFormattedString import javax.inject.Inject @@ -14,8 +14,6 @@ class SchoolAnnouncementAdapter @Inject constructor() : var items = emptyList() - var onItemClickListener: (SchoolAnnouncement) -> Unit = {} - override fun getItemCount() = items.size override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( @@ -28,9 +26,9 @@ class SchoolAnnouncementAdapter @Inject constructor() : with(holder.binding) { schoolAnnouncementItemDate.text = item.date.toFormattedString() schoolAnnouncementItemType.text = item.subject - schoolAnnouncementItemContent.text = item.content.parseUonetHtml() - - root.setOnClickListener { onItemClickListener(item) } + schoolAnnouncementItemContent.text = HtmlCompat.fromHtml( + item.content, HtmlCompat.FROM_HTML_MODE_COMPACT + ) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt deleted file mode 100644 index c1c58441..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.ui.modules.schoolannouncement - -import android.app.Dialog -import android.os.Bundle -import android.view.View -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.data.db.entities.SchoolAnnouncement -import io.github.wulkanowy.databinding.DialogSchoolAnnouncementBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.parseUonetHtml -import io.github.wulkanowy.utils.serializable -import io.github.wulkanowy.utils.toFormattedString - -@AndroidEntryPoint -class SchoolAnnouncementDialog : BaseDialogFragment() { - - private lateinit var announcement: SchoolAnnouncement - - companion object { - - private const val ARGUMENT_KEY = "item" - - fun newInstance(announcement: SchoolAnnouncement) = SchoolAnnouncementDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to announcement) - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - announcement = requireArguments().serializable(ARGUMENT_KEY) - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView( - DialogSchoolAnnouncementBinding.inflate(layoutInflater) - .apply { binding = this }.root - ) - .create() - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - with(binding) { - announcementDialogSubjectValue.text = announcement.subject - announcementDialogDateValue.text = announcement.date.toFormattedString() - announcementDialogDescriptionValue.text = announcement.content.parseUonetHtml() - - announcementDialogClose.setOnClickListener { dismiss() } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementFragment.kt index baf2824b..a5a53aef 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementFragment.kt @@ -8,7 +8,6 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.SchoolAnnouncement import io.github.wulkanowy.databinding.FragmentSchoolAnnouncementBinding import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.widgets.DividerItemDecoration import io.github.wulkanowy.utils.getThemeAttrColor @@ -44,9 +43,7 @@ class SchoolAnnouncementFragment : override fun initView() { with(binding.directorInformationRecycler) { layoutManager = LinearLayoutManager(context) - adapter = schoolAnnouncementAdapter.apply { - onItemClickListener = presenter::onItemClickListener - } + adapter = schoolAnnouncementAdapter addItemDecoration(DividerItemDecoration(context)) } with(binding) { @@ -102,10 +99,6 @@ class SchoolAnnouncementFragment : binding.directorInformationSwipe.isRefreshing = show } - override fun openSchoolAnnouncementDialog(item: SchoolAnnouncement) { - (activity as? MainActivity)?.showDialogFragment(SchoolAnnouncementDialog.newInstance(item)) - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt index f77a8833..ba89ab1f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt @@ -1,12 +1,14 @@ package io.github.wulkanowy.ui.modules.schoolannouncement -import io.github.wulkanowy.data.* -import io.github.wulkanowy.data.db.entities.SchoolAnnouncement +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -44,42 +46,50 @@ class SchoolAnnouncementPresenter @Inject constructor( view?.showErrorDetailsDialog(lastError) } - fun onItemClickListener(item: SchoolAnnouncement) { - view?.openSchoolAnnouncementDialog(item) - } - private fun loadData(forceRefresh: Boolean = false) { - flatResourceFlow { + Timber.i("Loading School announcement data started") + + flowWithResourceIn { val student = studentRepository.getCurrentStudent() schoolAnnouncementRepository.getSchoolAnnouncements(student, forceRefresh) - } - .logResourceStatus("load school announcement") - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) - updateData(it) + }.onEach { + when (it.status) { + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading School announcement result: Success") + view?.apply { + updateData(it.data!!.sortedByDescending { item -> item.date }) + showEmpty(it.data.isEmpty()) + showErrorView(false) + showContent(it.data.isNotEmpty()) + } + analytics.logEvent( + "load_school_announcement", + "items" to it.data!!.size + ) + } + Status.ERROR -> { + Timber.i("Loading School announcement result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceSuccess { - analytics.logEvent( - "load_school_announcement", - "items" to it.size - ) + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showProgress(false) - showRefresh(false) - } - } - .onResourceError(errorHandler::dispatch) - .launch("load_data") + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementView.kt index 383d0f29..0553df1e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementView.kt @@ -19,8 +19,6 @@ interface SchoolAnnouncementView : BaseView { fun setErrorDetails(message: String) - fun openSchoolAnnouncementDialog(item: SchoolAnnouncement) - fun showProgress(show: Boolean) fun enableSwipe(enable: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt index 21f56498..2612fab3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt @@ -6,7 +6,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.ui.modules.main.MainView import timber.log.Timber -class SettingsFragment : PreferenceFragmentCompat(), MainView.TitledView, SettingsView { +class SettingsFragment : PreferenceFragmentCompat(), MainView.TitledView { companion object { @@ -19,18 +19,4 @@ class SettingsFragment : PreferenceFragmentCompat(), MainView.TitledView, Settin setPreferencesFromResource(R.xml.scheme_preferences, rootKey) Timber.i("Settings view was initialized") } - - override fun showError(text: String, error: Throwable) {} - - override fun showMessage(text: String) {} - - override fun showExpiredDialog() {} - - override fun openClearLoginView() {} - - override fun showErrorDetailsDialog(error: Throwable) {} - - override fun showChangePasswordSnackbar(redirectUrl: String) {} - - override fun showAuthDialog() {} } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt deleted file mode 100644 index 79f91bc5..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings - -import io.github.wulkanowy.ui.base.BaseView - -interface SettingsView : BaseView \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt index 41e9e8b1..9f29731f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt @@ -4,11 +4,11 @@ import android.content.SharedPreferences import android.os.Bundle import android.view.View import androidx.preference.PreferenceFragmentCompat +import com.yariksoffice.lingver.Lingver import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.ErrorDialog -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.AppInfo import javax.inject.Inject @@ -24,6 +24,13 @@ class AdvancedFragment : PreferenceFragmentCompat(), @Inject lateinit var appInfo: AppInfo + @Inject + lateinit var lingver: Lingver + + companion object { + fun newInstance() = AdvancedFragment() + } + override val titleStringId get() = R.string.pref_settings_advanced_title override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -63,17 +70,13 @@ class AdvancedFragment : PreferenceFragmentCompat(), ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") - } - override fun onResume() { super.onResume() - preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) + preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) } override fun onPause() { super.onPause() - preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this) + preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt index 493ab5d7..a7ee800b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt @@ -9,7 +9,6 @@ import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.ErrorDialog -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.AppInfo import javax.inject.Inject @@ -28,6 +27,10 @@ class AppearanceFragment : PreferenceFragmentCompat(), @Inject lateinit var lingver: Lingver + companion object { + fun newInstance() = AppearanceFragment() + } + override val titleStringId get() = R.string.pref_settings_appearance_title override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -79,17 +82,13 @@ class AppearanceFragment : PreferenceFragmentCompat(), ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") - } - override fun onResume() { super.onResume() - preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) + preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) } override fun onPause() { super.onPause() - preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this) + preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/AppMenuItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/AppMenuItem.kt deleted file mode 100644 index f522913a..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/AppMenuItem.kt +++ /dev/null @@ -1,206 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.modules.Destination -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient - -@Serializable -sealed class AppMenuItem { - - companion object { - val defaultAppMenuItemList = setOf( - DashboardAppMenuItem(), - GradeAppMenuItem(), - TimetableAppMenuItem(), - AttendanceAppMenuItem(), - ExamsAppMenuItem(), - HomeworkAppMenuItem(), - NoteAppMenuItem(), - LuckyNumberAppMenuItem(), - SchoolAnnouncementsAppMenuItem(), - SchoolAndTeachersAppMenuItem(), - MobileDevicesAppMenuItem(), - ConferenceAppMenuItem(), - MessageAppMenuItem() - ).sortedBy { it.order } - } - - // https://youtrack.jetbrains.com/issue/KT-38958 - abstract var order: Int - - abstract val icon: Int - - abstract val title: Int - - abstract val destinationType: Destination.Type - - @Serializable - data class DashboardAppMenuItem(override var order: Int = 0) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_main_dashboard - - @Transient - override val title = R.string.dashboard_title - - @Transient - override val destinationType = Destination.Type.DASHBOARD - } - - @Serializable - data class GradeAppMenuItem(override var order: Int = 1) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_main_grade - - @Transient - override val title = R.string.grade_title - - @Transient - override val destinationType = Destination.Type.GRADE - } - - @Serializable - data class AttendanceAppMenuItem(override var order: Int = 2) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_main_attendance - - @Transient - override val title = R.string.attendance_title - - @Transient - override val destinationType = Destination.Type.ATTENDANCE - } - - @Serializable - data class TimetableAppMenuItem(override var order: Int = 3) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_main_timetable - - @Transient - override val title = R.string.timetable_title - - @Transient - override val destinationType = Destination.Type.TIMETABLE - } - - @Serializable - data class MessageAppMenuItem(override var order: Int = 4) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_messages - - @Transient - override val title = R.string.message_title - - @Transient - override val destinationType = Destination.Type.MESSAGE - } - - @Serializable - data class ExamsAppMenuItem(override var order: Int = 5) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_main_exam - - @Transient - override val title = R.string.exam_title - - @Transient - override val destinationType = Destination.Type.EXAM - } - - @Serializable - data class HomeworkAppMenuItem(override var order: Int = 6) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_homework - - @Transient - override val title = R.string.homework_title - - @Transient - override val destinationType = Destination.Type.HOMEWORK - } - - @Serializable - data class NoteAppMenuItem(override var order: Int = 7) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_note - - @Transient - override val title = R.string.note_title - - @Transient - override val destinationType = Destination.Type.NOTE - } - - @Serializable - data class LuckyNumberAppMenuItem(override var order: Int = 8) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_lucky_number - - @Transient - override val title = R.string.lucky_number_title - - @Transient - override val destinationType = Destination.Type.LUCKY_NUMBER - } - - @Serializable - data class ConferenceAppMenuItem(override var order: Int = 9) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_conferences - - @Transient - override val title = R.string.conferences_title - - @Transient - override val destinationType = Destination.Type.CONFERENCE - } - - @Serializable - data class SchoolAnnouncementsAppMenuItem(override var order: Int = 10) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_all_about - - @Transient - override val title = R.string.school_announcement_title - - @Transient - override val destinationType = Destination.Type.SCHOOL_ANNOUNCEMENT - } - - @Serializable - data class SchoolAndTeachersAppMenuItem(override var order: Int = 11) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_schoolandteachers - - @Transient - override val title = R.string.schoolandteachers_title - - @Transient - override val destinationType = Destination.Type.SCHOOL_AND_TEACHERS - } - - @Serializable - data class MobileDevicesAppMenuItem(override var order: Int = 12) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_mobile_devices - - @Transient - override val title = R.string.mobile_devices_title - - @Transient - override val destinationType = Destination.Type.MOBILE_DEVICE - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuItemMoveCallback.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuItemMoveCallback.kt deleted file mode 100644 index 49196fad..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuItemMoveCallback.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import androidx.recyclerview.widget.ItemTouchHelper -import androidx.recyclerview.widget.RecyclerView -import java.util.* - -class MenuItemMoveCallback( - private val menuOrderAdapter: MenuOrderAdapter, - private var onUserInteractionEndListener: (List) -> Unit = {} -) : ItemTouchHelper.Callback() { - - override fun isLongPressDragEnabled() = true - - override fun isItemViewSwipeEnabled() = false - - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { - //Not implemented - } - - override fun getMovementFlags( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder - ) = makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) - - override fun onMove( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder - ): Boolean { - val list = menuOrderAdapter.items.toMutableList() - - Collections.swap(list, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) - - menuOrderAdapter.submitList(list) - return true - } - - override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { - super.clearView(recyclerView, viewHolder) - - onUserInteractionEndListener(menuOrderAdapter.items.toList()) - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderAdapter.kt deleted file mode 100644 index 6bdd2fe0..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderAdapter.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.RecyclerView -import io.github.wulkanowy.databinding.ItemMenuOrderBinding -import javax.inject.Inject - -class MenuOrderAdapter @Inject constructor() : - RecyclerView.Adapter() { - - val items = mutableListOf() - - fun submitList(newItems: List) { - val diffResult = DiffUtil.calculateDiff(DiffCallback(newItems, items.toMutableList())) - - with(items) { - clear() - addAll(newItems) - } - - diffResult.dispatchUpdatesTo(this) - } - - override fun getItemCount() = items.size - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( - ItemMenuOrderBinding.inflate(LayoutInflater.from(parent.context), parent, false) - ) - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val item = items[position].appMenuItem - - with(holder.binding) { - menuOrderItemTitle.setText(item.title) - menuOrderItemIcon.setImageResource(item.icon) - } - } - - class ViewHolder(val binding: ItemMenuOrderBinding) : RecyclerView.ViewHolder(binding.root) - - private class DiffCallback( - private val oldList: List, - private val newList: List - ) : DiffUtil.Callback() { - - override fun getNewListSize() = newList.size - - override fun getOldListSize() = oldList.size - - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = - oldList[oldItemPosition] == newList[newItemPosition] - - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = - oldList[oldItemPosition].appMenuItem.destinationType == newList[newItemPosition].appMenuItem.destinationType - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderDividerItemDecoration.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderDividerItemDecoration.kt deleted file mode 100644 index 21a7f5e8..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderDividerItemDecoration.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import android.content.Context -import android.graphics.Canvas -import android.graphics.Rect -import android.graphics.drawable.ShapeDrawable -import android.view.View -import androidx.core.graphics.drawable.DrawableCompat -import androidx.core.view.forEach -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.RecyclerView -import io.github.wulkanowy.R -import io.github.wulkanowy.utils.getThemeAttrColor - -class MenuOrderDividerItemDecoration(private val context: Context) : - DividerItemDecoration(context, VERTICAL) { - - private val dividerDrawable = ShapeDrawable() - .apply { - DrawableCompat.setTint(this, context.getThemeAttrColor(R.attr.colorDivider)) - } - - override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) { - canvas.save() - val dividerLeft = parent.paddingLeft - val dividerRight = parent.width - parent.paddingRight - - parent.forEach { - if (parent.getChildAdapterPosition(it) == 3) { - val params = it.layoutParams as RecyclerView.LayoutParams - val dividerTop = it.bottom + params.bottomMargin - val dividerBottom = dividerTop + dividerDrawable.intrinsicHeight - - dividerDrawable.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom + 30) - dividerDrawable.draw(canvas) - } - } - - canvas.restore() - } - - override fun getItemOffsets( - outRect: Rect, view: View, parent: RecyclerView, - state: RecyclerView.State - ) { - if (parent.getChildAdapterPosition(view) == 3) { - outRect.bottom = dividerDrawable.intrinsicHeight + 30 - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderFragment.kt deleted file mode 100644 index e08fc5dd..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderFragment.kt +++ /dev/null @@ -1,101 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import androidx.activity.addCallback -import androidx.core.view.MenuProvider -import androidx.recyclerview.widget.ItemTouchHelper -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.SimpleItemAnimator -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.databinding.FragmentMenuOrderBinding -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.ui.widgets.DividerItemDecoration -import javax.inject.Inject - - -@AndroidEntryPoint -class MenuOrderFragment : BaseFragment(R.layout.fragment_menu_order), - MenuOrderView, MainView.TitledView { - - @Inject - lateinit var presenter: MenuOrderPresenter - - @Inject - lateinit var menuOrderAdapter: MenuOrderAdapter - - override val titleStringId = R.string.menu_order_title - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding = FragmentMenuOrderBinding.bind(view) - presenter.onAttachView(this) - } - - override fun initView() { - val itemTouchHelper = ItemTouchHelper( - MenuItemMoveCallback(menuOrderAdapter, presenter::onDragAndDropEnd) - ) - - itemTouchHelper.attachToRecyclerView(binding.menuOrderRecycler) - - with(binding.menuOrderRecycler) { - layoutManager = LinearLayoutManager(context) - adapter = menuOrderAdapter - addItemDecoration(MenuOrderDividerItemDecoration(context)) - addItemDecoration(DividerItemDecoration(context)) - (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false - } - - requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { - presenter.onBackSelected() - } - - initializeToolbar() - } - - private fun initializeToolbar() { - requireActivity().addMenuProvider(object : MenuProvider { - override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { - } - - override fun onMenuItemSelected(menuItem: MenuItem): Boolean { - if (menuItem.itemId == android.R.id.home) { - presenter.onBackSelected() - return true - } - return false - } - - }, viewLifecycleOwner) - } - - override fun updateData(data: List) { - menuOrderAdapter.submitList(data) - } - - override fun restartApp() { - startActivity(MainActivity.getStartIntent(requireContext())) - requireActivity().finishAffinity() - } - - override fun popView() { - (activity as? MainActivity?)?.popView() - } - - override fun showRestartConfirmationDialog() { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.menu_order_confirm_title) - .setMessage(R.string.menu_order_confirm_content) - .setPositiveButton(R.string.menu_order_confirm_restart) { _, _ -> presenter.onConfirmRestart() } - .setNegativeButton(R.string.all_cancel) { _, _ -> presenter.onCancelRestart() } - .show() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderItem.kt deleted file mode 100644 index b82bc1bd..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderItem.kt +++ /dev/null @@ -1,6 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -data class MenuOrderItem( - val appMenuItem: AppMenuItem, - val order: Int -) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderPresenter.kt deleted file mode 100644 index 1c90cc5e..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderPresenter.kt +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import timber.log.Timber -import javax.inject.Inject - -class MenuOrderPresenter @Inject constructor( - studentRepository: StudentRepository, - errorHandler: ErrorHandler, - private val preferencesRepository: PreferencesRepository -) : BasePresenter(errorHandler, studentRepository) { - - private var updatedMenuOrderItems = emptyList() - - override fun onAttachView(view: MenuOrderView) { - super.onAttachView(view) - view.initView() - Timber.i("Menu order view was initialized") - loadData() - } - - private fun loadData() { - val savedMenuItemList = (preferencesRepository.appMenuItemOrder) - .sortedBy { it.order } - .map { MenuOrderItem(it, it.order) } - - view?.updateData(savedMenuItemList) - } - - fun onDragAndDropEnd(list: List) { - val updatedList = list.mapIndexed { index, menuOrderItem -> - menuOrderItem.copy(order = index) - } - - updatedMenuOrderItems = updatedList - view?.updateData(updatedList) - } - - fun onBackSelected() { - if (updatedMenuOrderItems.isNotEmpty()) { - view?.showRestartConfirmationDialog() - } else { - view?.popView() - } - } - - fun onConfirmRestart() { - updatedMenuOrderItems.forEach { - it.appMenuItem.apply { - order = it.order - } - } - - preferencesRepository.appMenuItemOrder = updatedMenuOrderItems.map { it.appMenuItem } - view?.restartApp() - } - - fun onCancelRestart() { - view?.popView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderView.kt deleted file mode 100644 index 264e68cc..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderView.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import io.github.wulkanowy.ui.base.BaseView - -interface MenuOrderView : BaseView { - - fun initView() - - fun updateData(data: List) - - fun restartApp() - - fun showRestartConfirmationDialog() - - fun popView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt index 35c1faa4..0fc7e68e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt @@ -1,31 +1,29 @@ package io.github.wulkanowy.ui.modules.settings.notifications +import android.annotation.SuppressLint import android.content.Intent import android.content.SharedPreferences -import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build import android.os.Bundle +import android.provider.Settings import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.activity.result.contract.ActivityResultContracts -import androidx.core.app.NotificationManagerCompat -import androidx.core.content.ContextCompat +import androidx.appcompat.app.AlertDialog import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import androidx.preference.SwitchPreferenceCompat import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.thelittlefireman.appkillermanager.AppKillerManager import com.thelittlefireman.appkillermanager.exceptions.NoActionFoundException import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.ErrorDialog -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.openInternetBrowser -import io.github.wulkanowy.utils.openNotificationSettings +import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint @@ -39,41 +37,12 @@ class NotificationsFragment : PreferenceFragmentCompat(), @Inject lateinit var appInfo: AppInfo + companion object { + fun newInstance() = NotificationsFragment() + } + override val titleStringId get() = R.string.pref_settings_notifications_title - private val notificationsPermission = "android.permission.POST_NOTIFICATIONS" - - override val isNotificationPermissionGranted: Boolean - get() = ContextCompat.checkSelfPermission( - requireContext(), notificationsPermission - ) == PackageManager.PERMISSION_GRANTED - - override val isNotificationPiggybackPermissionGranted: Boolean - get() { - val packageNameList = - NotificationManagerCompat.getEnabledListenerPackages(requireContext()) - val appPackageName = requireContext().packageName - - return appPackageName in packageNameList - } - - private val requestPermissionLauncher = - registerForActivityResult(ActivityResultContracts.RequestPermission()) { - if (it) { - presenter.onNotificationsPermissionResult() - } else openNotificationsPermissionDialog() - } - - private val notificationSettingsPiggybackContract = - registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - presenter.onNotificationPiggybackPermissionResult() - } - - private val notificationSettingsExactAlarmsContract = - registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - presenter.onNotificationExactAlarmPermissionResult() - } - override fun initView(showDebugNotificationSwitch: Boolean) { findPreference(getString(R.string.pref_key_notification_debug))?.isVisible = showDebugNotificationSwitch @@ -88,18 +57,19 @@ class NotificationsFragment : PreferenceFragmentCompat(), } } - findPreference(getString(R.string.pref_key_notifications_system_settings)) - ?.setOnPreferenceClickListener { + findPreference(getString(R.string.pref_key_notifications_system_settings))?.run { + setOnPreferenceClickListener { presenter.onOpenSystemSettingsClicked() true } + } } override fun onCreateRecyclerView( - inflater: LayoutInflater, - parent: ViewGroup, + inflater: LayoutInflater?, + parent: ViewGroup?, state: Bundle? - ): RecyclerView = super.onCreateRecyclerView(inflater, parent, state) + ): RecyclerView? = super.onCreateRecyclerView(inflater, parent, state) .also { it.itemAnimator = null it.layoutAnimation = null @@ -149,16 +119,12 @@ class NotificationsFragment : PreferenceFragmentCompat(), ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") - } - override fun showFixSyncDialog() { - MaterialAlertDialogBuilder(requireContext()) + AlertDialog.Builder(requireContext()) .setTitle(R.string.pref_notify_fix_sync_issues) .setMessage(R.string.pref_notify_fix_sync_issues_message) .setNegativeButton(android.R.string.cancel) { _, _ -> } - .setPositiveButton(R.string.pref_notify_open_system_settings) { _, _ -> + .setPositiveButton(R.string.pref_notify_fix_sync_issues_settings_button) { _, _ -> try { AppKillerManager.doActionPowerSaving(requireContext()) AppKillerManager.doActionAutoStart(requireContext()) @@ -173,78 +139,31 @@ class NotificationsFragment : PreferenceFragmentCompat(), .show() } + @SuppressLint("InlinedApi") override fun openSystemSettings() { - requireActivity().openNotificationSettings() - } - - override fun requestNotificationPermissions() { - requestPermissionLauncher.launch(notificationsPermission) - } - - override fun openNotificationsPermissionDialog() { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.notifications_header_title) - .setMessage(R.string.notifications_header_description) - .setPositiveButton(R.string.pref_notification_go_to_settings) { _, _ -> - requireActivity().openNotificationSettings() + val intent = if (appInfo.systemVersion >= Build.VERSION_CODES.O) { + Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { + putExtra("android.provider.extra.APP_PACKAGE", requireActivity().packageName) } - .setNegativeButton(android.R.string.cancel) { _, _ -> - setNotificationPreferencesChecked(false) + } else { + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.fromParts("package", requireActivity().packageName, null) } - .setOnDismissListener { setNotificationPreferencesChecked(false) } - .show() - } - - override fun openNotificationPiggyBackPermissionDialog() { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.pref_notification_piggyback_popup_title)) - .setMessage(getString(R.string.pref_notification_piggyback_popup_description)) - .setPositiveButton(getString(R.string.pref_notification_go_to_settings)) { _, _ -> - notificationSettingsPiggybackContract.launch(Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")) - } - .setNegativeButton(android.R.string.cancel) { _, _ -> - setNotificationPiggybackPreferenceChecked(false) - } - .setOnDismissListener { setNotificationPiggybackPreferenceChecked(false) } - .show() - } - - override fun openNotificationExactAlarmSettings() { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.pref_notification_exact_alarm_popup_title)) - .setMessage(getString(R.string.pref_notification_exact_alarm_popup_descriptions)) - .setPositiveButton(getString(R.string.pref_notification_go_to_settings)) { _, _ -> - notificationSettingsExactAlarmsContract.launch(Intent("android.settings.REQUEST_SCHEDULE_EXACT_ALARM")) - } - .setNegativeButton(android.R.string.cancel) { _, _ -> - setUpcomingLessonsNotificationPreferenceChecked(false) - } - .setOnDismissListener { setUpcomingLessonsNotificationPreferenceChecked(false) } - .show() - } - - override fun setNotificationPreferencesChecked(isChecked: Boolean) { - findPreference(getString(R.string.pref_key_notifications_enable))?.isChecked = - isChecked - } - - override fun setNotificationPiggybackPreferenceChecked(isChecked: Boolean) { - findPreference(getString(R.string.pref_key_notifications_piggyback))?.isChecked = - isChecked - } - - override fun setUpcomingLessonsNotificationPreferenceChecked(isChecked: Boolean) { - findPreference(getString(R.string.pref_key_notifications_upcoming_lessons_enable))?.isChecked = - isChecked + } + try { + requireActivity().startActivity(intent) + } catch (e: Exception) { + Timber.e(e) + } } override fun onResume() { super.onResume() - preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) + preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) } override fun onPause() { super.onPause() - preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this) + preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt index 232b0348..8366d309 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt @@ -26,15 +26,11 @@ class NotificationsPresenter @Inject constructor( with(view) { enableNotification( - notificationKey = preferencesRepository.notificationsEnableKey, - enable = preferencesRepository.isServiceEnabled + preferencesRepository.notificationsEnableKey, + preferencesRepository.isServiceEnabled ) initView(appInfo.isDebug) } - - checkNotificationsPermissionState() - checkNotificationPiggybackState() - Timber.i("Settings notifications view was initialized") } @@ -43,26 +39,14 @@ class NotificationsPresenter @Inject constructor( preferencesRepository.apply { when (key) { - isUpcomingLessonsNotificationsEnableKey, isUpcomingLessonsNotificationsPersistentKey -> { + isUpcomingLessonsNotificationsEnableKey -> { if (!isUpcomingLessonsNotificationsEnable) { timetableNotificationHelper.cancelNotification() - } else if (!timetableNotificationHelper.canScheduleExactAlarms()) { - view?.openNotificationExactAlarmSettings() - } - } - notificationsEnableKey -> { - if (isNotificationsEnable && view?.isNotificationPermissionGranted == false) { - view?.requestNotificationPermissions() } } isDebugNotificationEnableKey -> { chuckerCollector.showNotification = isDebugNotificationEnable } - isNotificationPiggybackEnabledKey -> { - if (isNotificationPiggybackEnabled && view?.isNotificationPiggybackPermissionGranted == false) { - view?.openNotificationPiggyBackPermissionDialog() - } - } } } analytics.logEvent("setting_changed", "name" to key) @@ -75,36 +59,4 @@ class NotificationsPresenter @Inject constructor( fun onOpenSystemSettingsClicked() { view?.openSystemSettings() } - - fun onNotificationsPermissionResult() { - view?.run { - setNotificationPreferencesChecked(isNotificationPermissionGranted) - } - } - - fun onNotificationPiggybackPermissionResult() { - view?.run { - setNotificationPiggybackPreferenceChecked(isNotificationPiggybackPermissionGranted) - } - } - - fun onNotificationExactAlarmPermissionResult() { - view?.setUpcomingLessonsNotificationPreferenceChecked(timetableNotificationHelper.canScheduleExactAlarms()) - } - - private fun checkNotificationsPermissionState() { - if (preferencesRepository.isNotificationsEnable) { - view?.run { - setNotificationPreferencesChecked(isNotificationPermissionGranted) - } - } - } - - private fun checkNotificationPiggybackState() { - if (preferencesRepository.isNotificationPiggybackEnabled) { - view?.run { - setNotificationPiggybackPreferenceChecked(isNotificationPiggybackPermissionGranted) - } - } - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsView.kt index a391681c..2ab9b035 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsView.kt @@ -4,10 +4,6 @@ import io.github.wulkanowy.ui.base.BaseView interface NotificationsView : BaseView { - val isNotificationPermissionGranted: Boolean - - val isNotificationPiggybackPermissionGranted: Boolean - fun initView(showDebugNotificationSwitch: Boolean) fun showFixSyncDialog() @@ -15,18 +11,4 @@ interface NotificationsView : BaseView { fun openSystemSettings() fun enableNotification(notificationKey: String, enable: Boolean) - - fun requestNotificationPermissions() - - fun openNotificationsPermissionDialog() - - fun openNotificationPiggyBackPermissionDialog() - - fun openNotificationExactAlarmSettings() - - fun setNotificationPreferencesChecked(isChecked: Boolean) - - fun setNotificationPiggybackPreferenceChecked(isChecked: Boolean) - - fun setUpcomingLessonsNotificationPreferenceChecked(isChecked: Boolean) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt index df2e1348..83caa3b0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt @@ -5,12 +5,10 @@ import android.os.Bundle import android.view.View import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.ErrorDialog -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.ui.modules.main.MainView import javax.inject.Inject @@ -22,6 +20,10 @@ class SyncFragment : PreferenceFragmentCompat(), @Inject lateinit var presenter: SyncPresenter + companion object { + fun newInstance() = SyncFragment() + } + override val titleStringId get() = R.string.pref_settings_sync_title override val syncSuccessString get() = getString(R.string.pref_services_message_sync_success) @@ -77,11 +79,7 @@ class SyncFragment : PreferenceFragmentCompat(), } override fun showMessage(text: String) { - Snackbar.make(requireView(), text, Snackbar.LENGTH_LONG) - .apply { - anchorView = requireActivity().findViewById(R.id.main_bottom_nav) - show() - } + (activity as? BaseActivity<*, *>)?.showMessage(text) } override fun showExpiredDialog() { @@ -97,20 +95,16 @@ class SyncFragment : PreferenceFragmentCompat(), } override fun showErrorDetailsDialog(error: Throwable) { - ErrorDialog.newInstance(error).show(childFragmentManager, "error_details") - } - - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") + ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } override fun onResume() { super.onResume() - preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) + preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) } override fun onPause() { super.onPause() - preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this) + preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt index 1ecb4a6e..63e86a47 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt @@ -46,7 +46,7 @@ class SyncPresenter @Inject constructor( fun onSyncNowClicked() { view?.run { syncManager.startOneTimeSyncWorker().onEach { workInfo -> - when (workInfo?.state) { + when (workInfo.state) { WorkInfo.State.ENQUEUED -> { setSyncInProgress(true) Timber.i("Setting sync now started") @@ -59,16 +59,13 @@ class SyncPresenter @Inject constructor( WorkInfo.State.FAILED -> { showError( syncFailedString, - Throwable( - message = workInfo.outputData.getString("error_message"), - cause = Throwable(workInfo.outputData.getString("error_stack")) - ) + Throwable(workInfo.outputData.getString("error")) ) analytics.logEvent("sync_now", "status" to "failed") } - else -> Timber.d("Sync now state: ${workInfo?.state}") + else -> Timber.d("Sync now state: ${workInfo.state}") } - if (workInfo?.state?.isFinished == true) { + if (workInfo.state.isFinished) { setSyncInProgress(false) setSyncDateInView() } @@ -79,7 +76,9 @@ class SyncPresenter @Inject constructor( } private fun setSyncDateInView() { - val lastSyncDate = preferencesRepository.lasSyncDate ?: return + val lastSyncDate = preferencesRepository.lasSyncDate + + if (lastSyncDate.year == 1970) return view?.setLastSyncDate(lastSyncDate.toFormattedString("dd.MM.yyyy HH:mm:ss")) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt index cfb62849..376ef374 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt @@ -1,56 +1,29 @@ package io.github.wulkanowy.ui.modules.splash -import android.annotation.SuppressLint -import android.content.Context -import android.content.Intent import android.os.Bundle import android.widget.Toast import android.widget.Toast.LENGTH_LONG -import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.viewbinding.ViewBinding import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.services.shortcuts.ShortcutsHelper import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.openInternetBrowser -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json import javax.inject.Inject -@SuppressLint("CustomSplashScreen") @AndroidEntryPoint class SplashActivity : BaseActivity(), SplashView { @Inject - override lateinit var presenter: SplashPresenter + lateinit var appInfo: AppInfo @Inject - lateinit var shortcutsHelper: ShortcutsHelper - - companion object { - - private const val EXTRA_START_DESTINATION = "start_destination_json" - - private const val EXTRA_EXTERNAL_URL = "external_url" - - fun getStartIntent(context: Context, destination: Destination? = null) = - Intent(context, SplashActivity::class.java).apply { - destination?.let { putExtra(EXTRA_START_DESTINATION, Json.encodeToString(it)) } - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - } - } + override lateinit var presenter: SplashPresenter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - installSplashScreen().setKeepOnScreenCondition { true } - shortcutsHelper.initializeShortcuts() - - val externalLink = intent?.getStringExtra(EXTRA_EXTERNAL_URL) - val startDestinationJson = intent?.getStringExtra(EXTRA_START_DESTINATION) - - presenter.onAttachView(this, externalLink, startDestinationJson) + presenter.onAttachView(this, intent?.getStringExtra("external_url")) } override fun openLoginView() { @@ -58,8 +31,8 @@ class SplashActivity : BaseActivity(), SplashView finish() } - override fun openMainView(destination: Destination?) { - startActivity(MainActivity.getStartIntent(this, destination)) + override fun openMainView() { + startActivity(MainActivity.getStartIntent(this)) finish() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt index 767c885c..03e43efa 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt @@ -1,40 +1,36 @@ package io.github.wulkanowy.ui.modules.splash +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.ui.modules.Destination -import kotlinx.coroutines.launch -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json +import io.github.wulkanowy.utils.flowWithResource +import kotlinx.coroutines.flow.onEach +import timber.log.Timber import javax.inject.Inject class SplashPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, - private val json: Json ) : BasePresenter(errorHandler, studentRepository) { - fun onAttachView(view: SplashView, externalUrl: String?, startDestinationJson: String?) { + fun onAttachView(view: SplashView, externalUrl: String?) { super.onAttachView(view) - val startDestination: Destination? = startDestinationJson?.let { json.decodeFromString(it) } - if (!externalUrl.isNullOrBlank()) { view.openExternalUrlAndFinish(externalUrl) return } - presenterScope.launch { - runCatching { studentRepository.isCurrentStudentSet() } - .onFailure(errorHandler::dispatch) - .onSuccess { - if (it) { - view.openMainView(startDestination) - } else { - view.openLoginView() - } + flowWithResource { studentRepository.isCurrentStudentSet() }.onEach { + when (it.status) { + Status.LOADING -> Timber.d("Is current user set check started") + Status.SUCCESS -> { + if (it.data!!) view.openMainView() + else view.openLoginView() } - } + Status.ERROR -> errorHandler.dispatch(it.error!!) + } + }.launch() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt index 1c5d8bfd..a5aa1409 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt @@ -1,13 +1,12 @@ package io.github.wulkanowy.ui.modules.splash import io.github.wulkanowy.ui.base.BaseView -import io.github.wulkanowy.ui.modules.Destination interface SplashView : BaseView { fun openLoginView() - fun openMainView(destination: Destination?) + fun openMainView() fun openExternalUrlAndFinish(url: String) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt index 60912200..2d838749 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt @@ -13,7 +13,7 @@ class StudentInfoAdapter @Inject constructor() : var items = listOf() - var onItemClickListener: (StudentInfoView.Type?) -> Unit = {} + var onItemClickListener: (position: Int) -> Unit = {} var onItemLongClickListener: (text: String) -> Unit = {} @@ -32,7 +32,7 @@ class StudentInfoAdapter @Inject constructor() : studentInfoItemArrow.visibility = if (item.showArrow) VISIBLE else GONE with(root) { - setOnClickListener { onItemClickListener(item.viewType) } + setOnClickListener { onItemClickListener(position) } setOnLongClickListener { onItemLongClickListener(studentInfoItemSubtitle.text.toString()) true diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt index 598046a2..e9ef4137 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.studentinfo +import android.annotation.SuppressLint import android.content.ClipData import android.content.ClipboardManager import android.os.Bundle @@ -8,7 +9,6 @@ import android.view.MenuInflater import android.view.View import android.widget.Toast import androidx.core.content.getSystemService -import androidx.core.os.bundleOf import androidx.core.view.get import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager @@ -25,8 +25,6 @@ import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.capitalise import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.nullableSerializable -import io.github.wulkanowy.utils.serializable import javax.inject.Inject @AndroidEntryPoint @@ -41,9 +39,7 @@ class StudentInfoFragment : lateinit var studentInfoAdapter: StudentInfoAdapter override val titleStringId: Int - get() = when ( - requireArguments().nullableSerializable(INFO_TYPE_ARGUMENT_KEY) - ) { + get() = when (requireArguments().getSerializable(INFO_TYPE_ARGUMENT_KEY) as? StudentInfoView.Type) { StudentInfoView.Type.PERSONAL -> R.string.account_personal_data StudentInfoView.Type.CONTACT -> R.string.account_contact StudentInfoView.Type.ADDRESS -> R.string.account_address @@ -63,14 +59,13 @@ class StudentInfoFragment : fun newInstance(type: StudentInfoView.Type, studentWithSemesters: StudentWithSemesters) = StudentInfoFragment().apply { - arguments = bundleOf( - INFO_TYPE_ARGUMENT_KEY to type, - STUDENT_ARGUMENT_KEY to studentWithSemesters - ) + arguments = Bundle().apply { + putSerializable(INFO_TYPE_ARGUMENT_KEY, type) + putSerializable(STUDENT_ARGUMENT_KEY, studentWithSemesters) + } } } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -80,9 +75,9 @@ class StudentInfoFragment : super.onViewCreated(view, savedInstanceState) binding = FragmentStudentInfoBinding.bind(view) presenter.onAttachView( - view = this, - type = requireArguments().serializable(INFO_TYPE_ARGUMENT_KEY), - studentWithSemesters = requireArguments().serializable(STUDENT_ARGUMENT_KEY), + this, + requireArguments().getSerializable(INFO_TYPE_ARGUMENT_KEY) as StudentInfoView.Type, + requireArguments().getSerializable(STUDENT_ARGUMENT_KEY) as StudentWithSemesters ) } @@ -135,9 +130,9 @@ class StudentInfoFragment : getString(R.string.student_info_parents_name) to studentInfo.parentsNames ).map { StudentInfoItem( - title = it.first, - subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, - showArrow = false, + it.first, + it.second.ifBlank { getString(R.string.all_no_data) }, + false, ) } ) @@ -151,32 +146,25 @@ class StudentInfoFragment : getString(R.string.student_info_email) to studentInfo.email ).map { StudentInfoItem( - title = it.first, - subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, - showArrow = false, + it.first, + it.second.ifBlank { getString(R.string.all_no_data) }, + false, ) } ) } + @SuppressLint("DefaultLocale") override fun showFamilyTypeData(studentInfo: StudentInfo) { - val items = buildList { - add(studentInfo.firstGuardian?.let { - Triple(it.kinship.capitalise(), it.fullName, StudentInfoView.Type.FIRST_GUARDIAN) - }) - - add(studentInfo.secondGuardian?.let { - Triple(it.kinship.capitalise(), it.fullName, StudentInfoView.Type.SECOND_GUARDIAN) - }) - }.filterNotNull() - updateData( - items.map { (title, value, type) -> + listOfNotNull( + studentInfo.firstGuardian?.let { it.kinship.capitalise() to it.fullName }, + studentInfo.secondGuardian?.let { it.kinship.capitalise() to it.fullName }, + ).map { (title, value) -> StudentInfoItem( - title = title.ifBlank { getString(R.string.all_no_data) }, - subtitle = value.ifBlank { getString(R.string.all_no_data) }, - showArrow = true, - viewType = type, + title.ifBlank { getString(R.string.all_no_data) }, + value.ifBlank { getString(R.string.all_no_data) }, + true, ) } ) @@ -190,15 +178,15 @@ class StudentInfoFragment : getString(R.string.student_info_correspondence_address) to studentInfo.correspondenceAddress ).map { StudentInfoItem( - title = it.first, - subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, - showArrow = false, + it.first, + it.second.ifBlank { getString(R.string.all_no_data) }, + false, ) } ) } - override fun showGuardianTypeData(studentGuardian: StudentGuardian) { + override fun showFirstGuardianTypeData(studentGuardian: StudentGuardian) { updateData( listOf( getString(R.string.student_info_full_name) to studentGuardian.fullName, @@ -208,9 +196,27 @@ class StudentInfoFragment : getString(R.string.student_info_email) to studentGuardian.email ).map { StudentInfoItem( - title = it.first, - subtitle = it.second.ifBlank { getString(R.string.all_no_data) }, - showArrow = false, + it.first, + it.second.ifBlank { getString(R.string.all_no_data) }, + false, + ) + } + ) + } + + override fun showSecondGuardianTypeData(studentGuardian: StudentGuardian) { + updateData( + listOf( + getString(R.string.student_info_full_name) to studentGuardian.fullName, + getString(R.string.student_info_kinship) to studentGuardian.kinship, + getString(R.string.student_info_guardian_address) to studentGuardian.address, + getString(R.string.student_info_phones) to studentGuardian.phones, + getString(R.string.student_info_email) to studentGuardian.email + ).map { + StudentInfoItem( + it.first, + it.second.ifBlank { getString(R.string.all_no_data) }, + false, ) } ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoItem.kt index 21226539..bb149b2b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoItem.kt @@ -3,6 +3,5 @@ package io.github.wulkanowy.ui.modules.studentinfo data class StudentInfoItem( val title: String, val subtitle: String, - val showArrow: Boolean, - val viewType: StudentInfoView.Type? = null, + val showArrow: Boolean ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt index 083b590b..55ac66d0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.studentinfo -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.StudentInfo import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.repositories.StudentInfoRepository @@ -8,7 +8,10 @@ import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.getCurrentOrLast +import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -55,12 +58,13 @@ class StudentInfoPresenter @Inject constructor( view?.showErrorDetailsDialog(lastError) } - fun onItemSelected(viewType: StudentInfoView.Type?) { - viewType ?: return + fun onItemSelected(position: Int) { + if (infoType != StudentInfoView.Type.FAMILY) return view?.openStudentInfoView( - studentWithSemesters = studentWithSemesters, - infoType = viewType, + if (position == 0) StudentInfoView.Type.FIRST_GUARDIAN + else StudentInfoView.Type.SECOND_GUARDIAN, + studentWithSemesters ) } @@ -69,50 +73,47 @@ class StudentInfoPresenter @Inject constructor( } private fun loadData(forceRefresh: Boolean = false) { - flatResourceFlow { + flowWithResourceIn { val semester = studentWithSemesters.semesters.getCurrentOrLast() studentInfoRepository.getStudentInfo( - student = studentWithSemesters.student, - semester = semester, - forceRefresh = forceRefresh + studentWithSemesters.student, + semester, + forceRefresh ) - } - .logResourceStatus("load student info $infoType") - .onResourceData { - val isFamily = infoType == StudentInfoView.Type.FAMILY - val isFirstGuardianEmpty = it?.firstGuardian == null - val isSecondGuardianEmpty = it?.secondGuardian == null - if (it != null && !(isFamily && isFirstGuardianEmpty && isSecondGuardianEmpty)) { - Timber.i("Loading student info $infoType result: Success") - showCorrectData(it) - view?.run { - showContent(true) - showEmpty(false) - showErrorView(false) - } - } else { - Timber.i("Loading student info $infoType result: No student or family info found") - view?.run { - showContent(!isViewEmpty) - showEmpty(isViewEmpty) - showErrorView(false) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading student info $infoType started") + Status.SUCCESS -> { + if (it.data != null && !(infoType == StudentInfoView.Type.FAMILY && it.data.firstGuardian == null && it.data.secondGuardian == null)) { + Timber.i("Loading student info $infoType result: Success") + showCorrectData(it.data) + view?.run { + showContent(true) + showEmpty(false) + showErrorView(false) + } + analytics.logEvent("load_item", "type" to "student_info") + } else { + Timber.i("Loading student info $infoType result: No student or family info found") + view?.run { + showContent(!isViewEmpty) + showEmpty(isViewEmpty) + showErrorView(false) + } } } - } - .onResourceSuccess { - if (it != null) { - analytics.logEvent("load_item", "type" to "student_info") + Status.ERROR -> { + Timber.i("Loading student info $infoType result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceNotLoading { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } + }.afterLoading { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun showCorrectData(studentInfo: StudentInfo) { @@ -121,8 +122,8 @@ class StudentInfoPresenter @Inject constructor( StudentInfoView.Type.CONTACT -> view?.showContactTypeData(studentInfo) StudentInfoView.Type.ADDRESS -> view?.showAddressTypeData(studentInfo) StudentInfoView.Type.FAMILY -> view?.showFamilyTypeData(studentInfo) - StudentInfoView.Type.SECOND_GUARDIAN -> view?.showGuardianTypeData(studentInfo.secondGuardian!!) - StudentInfoView.Type.FIRST_GUARDIAN -> view?.showGuardianTypeData(studentInfo.firstGuardian!!) + StudentInfoView.Type.SECOND_GUARDIAN -> view?.showSecondGuardianTypeData(studentInfo.secondGuardian!!) + StudentInfoView.Type.FIRST_GUARDIAN -> view?.showFirstGuardianTypeData(studentInfo.firstGuardian!!) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt index c20359df..87613e62 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt @@ -25,7 +25,9 @@ interface StudentInfoView : BaseView { fun showFamilyTypeData(studentInfo: StudentInfo) - fun showGuardianTypeData(studentGuardian: StudentGuardian) + fun showFirstGuardianTypeData(studentGuardian: StudentGuardian) + + fun showSecondGuardianTypeData(studentGuardian: StudentGuardian) fun openStudentInfoView(infoType: Type, studentWithSemesters: StudentWithSemesters) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt index d917e7d5..87b3362d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt @@ -1,69 +1,119 @@ package io.github.wulkanowy.ui.modules.timetable +import android.graphics.Paint import android.view.LayoutInflater import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.TextView -import androidx.core.view.isVisible +import androidx.core.view.ViewCompat import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.databinding.ItemTimetableBinding import io.github.wulkanowy.databinding.ItemTimetableSmallBinding import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.isJustFinished +import io.github.wulkanowy.utils.isShowTimeUntil +import io.github.wulkanowy.utils.left import io.github.wulkanowy.utils.toFormattedString +import io.github.wulkanowy.utils.until +import timber.log.Timber +import java.time.LocalDateTime +import java.util.Timer import javax.inject.Inject +import kotlin.concurrent.timer -class TimetableAdapter @Inject constructor() : - ListAdapter(differ) { +class TimetableAdapter @Inject constructor() : RecyclerView.Adapter() { - override fun getItemViewType(position: Int): Int = getItem(position).type.ordinal + private enum class ViewType { + ITEM_NORMAL, + ITEM_SMALL + } + + var onClickListener: (Timetable) -> Unit = {} + + private var showWholeClassPlan: String = "no" + + private var showGroupsInPlan: Boolean = false + + private var showTimers: Boolean = false + + private val timers = mutableMapOf() + + private val items = mutableListOf() + + fun submitList( + newTimetable: List, + showWholeClassPlan: String = this.showWholeClassPlan, + showGroupsInPlan: Boolean = this.showGroupsInPlan, + showTimers: Boolean = this.showTimers + ) { + val isFlagsDifferent = this.showWholeClassPlan != showWholeClassPlan + || this.showGroupsInPlan != showGroupsInPlan + || this.showTimers != showTimers + + val diffResult = DiffUtil.calculateDiff( + TimetableAdapterDiffCallback( + oldList = items.toMutableList(), + newList = newTimetable, + isFlagsDifferent = isFlagsDifferent + ) + ) + + this.showGroupsInPlan = showGroupsInPlan + this.showTimers = showTimers + this.showWholeClassPlan = showWholeClassPlan + + items.clear() + items.addAll(newTimetable) + + diffResult.dispatchUpdatesTo(this) + } + + fun clearTimers() { + Timber.d("Timetable timers (${timers.size}) cleared") + with(timers) { + forEach { (_, timer) -> + timer?.cancel() + timer?.purge() + } + clear() + } + } + + override fun getItemCount() = items.size + + override fun getItemViewType(position: Int) = when { + !items[position].isStudentPlan && showWholeClassPlan == "small" -> ViewType.ITEM_SMALL.ordinal + else -> ViewType.ITEM_NORMAL.ordinal + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val inflater = LayoutInflater.from(parent.context) - return when (TimetableItemType.values()[viewType]) { - TimetableItemType.SMALL -> SmallViewHolder( - ItemTimetableSmallBinding.inflate(inflater, parent, false) - ) - TimetableItemType.NORMAL -> NormalViewHolder( + return when (viewType) { + ViewType.ITEM_NORMAL.ordinal -> ItemViewHolder( ItemTimetableBinding.inflate(inflater, parent, false) ) + ViewType.ITEM_SMALL.ordinal -> SmallItemViewHolder( + ItemTimetableSmallBinding.inflate(inflater, parent, false) + ) + else -> throw IllegalStateException() } } - override fun onBindViewHolder( - holder: RecyclerView.ViewHolder, - position: Int, - payloads: MutableList - ) { - if (payloads.isEmpty()) return super.onBindViewHolder(holder, position, payloads) - - if (holder is NormalViewHolder) updateTimeLeft( - binding = holder.binding, - timeLeft = (getItem(position) as TimetableItem.Normal).timeLeft, - ) - } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val lesson = items[position] + when (holder) { - is SmallViewHolder -> bindSmallView( - binding = holder.binding, - item = getItem(position) as TimetableItem.Small, - ) - is NormalViewHolder -> bindNormalView( - binding = holder.binding, - item = getItem(position) as TimetableItem.Normal, - ) + is ItemViewHolder -> bindNormalView(holder.binding, lesson, position) + is SmallItemViewHolder -> bindSmallView(holder.binding, lesson) } } - private fun bindSmallView(binding: ItemTimetableSmallBinding, item: TimetableItem.Small) { - val lesson = item.lesson - + private fun bindSmallView(binding: ItemTimetableSmallBinding, lesson: Timetable) { with(binding) { timetableSmallItemNumber.text = lesson.number.toString() timetableSmallItemSubject.text = lesson.subject @@ -75,13 +125,11 @@ class TimetableAdapter @Inject constructor() : bindSmallDescription(binding, lesson) bindSmallColors(binding, lesson) - root.setOnClickListener { item.onClick(lesson) } + root.setOnClickListener { onClickListener(lesson) } } } - private fun bindNormalView(binding: ItemTimetableBinding, item: TimetableItem.Normal) { - val lesson = item.lesson - + private fun bindNormalView(binding: ItemTimetableBinding, lesson: Timetable, position: Int) { with(binding) { timetableItemNumber.text = lesson.number.toString() timetableItemSubject.text = lesson.subject @@ -92,47 +140,95 @@ class TimetableAdapter @Inject constructor() : timetableItemTimeFinish.text = lesson.end.toFormattedString("HH:mm") bindSubjectStyle(timetableItemSubject, lesson) - bindNormalDescription(binding, item) + bindNormalDescription(binding, lesson) bindNormalColors(binding, lesson) - updateTimeLeft(binding, item.timeLeft) - root.setOnClickListener { item.onClick(lesson) } + timers[position]?.let { + it.cancel() + it.purge() + } + timers[position] = null + + if (lesson.isStudentPlan && showTimers) { + timers[position] = timer(period = 1000) { + if (ViewCompat.isAttachedToWindow(root)) { + root.post { updateTimeLeft(binding, lesson, position) } + } + } + } else { + // reset item on set changed + timetableItemTimeUntil.visibility = GONE + timetableItemTimeLeft.visibility = GONE + } + + root.setOnClickListener { onClickListener(lesson) } } } - private fun updateTimeLeft(binding: ItemTimetableBinding, timeLeft: TimeLeft?) { + private fun getPreviousLesson(position: Int): LocalDateTime? { + return items.filter { it.isStudentPlan } + .getOrNull(position - 1 - items.filterIndexed { i, item -> i < position && !item.isStudentPlan }.size) + ?.let { + if (!it.canceled && it.isStudentPlan) it.end + else null + } + } + + private fun updateTimeLeft(binding: ItemTimetableBinding, lesson: Timetable, position: Int) { + val isShowTimeUntil = lesson.isShowTimeUntil(getPreviousLesson(position)) + val until = lesson.until + val left = lesson.left + val isJustFinished = lesson.isJustFinished + with(binding) { when { // before lesson - timeLeft?.until != null -> { + isShowTimeUntil -> { + Timber.d("Show time until lesson: $position") timetableItemTimeLeft.visibility = GONE with(timetableItemTimeUntil) { visibility = VISIBLE text = context.getString( R.string.timetable_time_until, - context.getString( - R.string.timetable_minutes, - timeLeft.until.toMinutes().toString(10) - ) + if (until.seconds <= 60) { + context.getString( + R.string.timetable_seconds, + until.seconds.toString(10) + ) + } else { + context.getString( + R.string.timetable_minutes, + until.toMinutes().toString(10) + ) + } ) } } // after lesson start - timeLeft?.left != null -> { + left != null -> { + Timber.d("Show time left lesson: $position") timetableItemTimeUntil.visibility = GONE with(timetableItemTimeLeft) { visibility = VISIBLE text = context.getString( R.string.timetable_time_left, - context.getString( - R.string.timetable_minutes, - timeLeft.left.toMinutes().toString() - ) + if (left.seconds < 60) { + context.getString( + R.string.timetable_seconds, + left.seconds.toString(10) + ) + } else { + context.getString( + R.string.timetable_minutes, + left.toMinutes().toString(10) + ) + } ) } } // right after lesson finish - timeLeft?.isJustFinished == true -> { + isJustFinished -> { + Timber.d("Show just finished lesson: $position") timetableItemTimeUntil.visibility = GONE timetableItemTimeLeft.visibility = VISIBLE timetableItemTimeLeft.text = root.context.getString(R.string.timetable_finished) @@ -146,7 +242,9 @@ class TimetableAdapter @Inject constructor() : } private fun bindSubjectStyle(subjectView: TextView, lesson: Timetable) { - subjectView.paint.isStrikeThruText = lesson.canceled + subjectView.paintFlags = + if (lesson.canceled) subjectView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG + else subjectView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() } private fun bindSmallDescription(binding: ItemTimetableSmallBinding, lesson: Timetable) { @@ -160,7 +258,7 @@ class TimetableAdapter @Inject constructor() : timetableSmallItemDescription.setTextColor( root.context.getThemeAttrColor( - if (lesson.canceled) R.attr.colorTimetableCanceled + if (lesson.canceled) R.attr.colorPrimary else R.attr.colorTimetableChange ) ) @@ -172,8 +270,7 @@ class TimetableAdapter @Inject constructor() : } } - private fun bindNormalDescription(binding: ItemTimetableBinding, item: TimetableItem.Normal) { - val lesson = item.lesson + private fun bindNormalDescription(binding: ItemTimetableBinding, lesson: Timetable) { with(binding) { if (lesson.info.isNotBlank() && !lesson.changes) { timetableItemDescription.visibility = VISIBLE @@ -185,14 +282,15 @@ class TimetableAdapter @Inject constructor() : timetableItemDescription.setTextColor( root.context.getThemeAttrColor( - if (lesson.canceled) R.attr.colorTimetableCanceled + if (lesson.canceled) R.attr.colorPrimary else R.attr.colorTimetableChange ) ) } else { timetableItemDescription.visibility = GONE - timetableItemRoom.isVisible = lesson.room.isNotBlank() || lesson.roomOld.isNotBlank() - timetableItemGroup.isVisible = item.showGroupsInPlan && lesson.group.isNotBlank() + timetableItemRoom.visibility = VISIBLE + timetableItemGroup.visibility = + if (showGroupsInPlan && lesson.group.isNotBlank()) VISIBLE else GONE timetableItemTeacher.visibility = VISIBLE } } @@ -228,8 +326,8 @@ class TimetableAdapter @Inject constructor() : } private fun updateNumberAndSubjectCanceledColor(numberView: TextView, subjectView: TextView) { - numberView.setTextColor(numberView.context.getThemeAttrColor(R.attr.colorTimetableCanceled)) - subjectView.setTextColor(subjectView.context.getThemeAttrColor(R.attr.colorTimetableCanceled)) + numberView.setTextColor(numberView.context.getThemeAttrColor(R.attr.colorPrimary)) + subjectView.setTextColor(subjectView.context.getThemeAttrColor(R.attr.colorPrimary)) } private fun updateNumberColor(numberView: TextView, lesson: Timetable) { @@ -262,41 +360,32 @@ class TimetableAdapter @Inject constructor() : private fun updateTeacherColor(teacherTextView: TextView, lesson: Timetable) { teacherTextView.setTextColor( teacherTextView.context.getThemeAttrColor( - if (lesson.teacherOld.isNotBlank()) R.attr.colorTimetableChange + if (lesson.teacherOld.isNotBlank() && lesson.teacherOld != lesson.teacher) R.attr.colorTimetableChange else android.R.attr.textColorSecondary ) ) } - private class NormalViewHolder(val binding: ItemTimetableBinding) : + private class ItemViewHolder(val binding: ItemTimetableBinding) : RecyclerView.ViewHolder(binding.root) - private class SmallViewHolder(val binding: ItemTimetableSmallBinding) : + private class SmallItemViewHolder(val binding: ItemTimetableSmallBinding) : RecyclerView.ViewHolder(binding.root) - companion object { - private val differ = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: TimetableItem, newItem: TimetableItem): Boolean = - when { - oldItem is TimetableItem.Small && newItem is TimetableItem.Small -> { - oldItem.lesson.start == newItem.lesson.start - } - oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal -> { - oldItem.lesson.start == newItem.lesson.start - } - else -> oldItem == newItem - } + class TimetableAdapterDiffCallback( + private val oldList: List, + private val newList: List, + private val isFlagsDifferent: Boolean + ) : DiffUtil.Callback() { - override fun areContentsTheSame(oldItem: TimetableItem, newItem: TimetableItem) = - oldItem == newItem + override fun getOldListSize() = oldList.size - override fun getChangePayload(oldItem: TimetableItem, newItem: TimetableItem): Any? { - return if (oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal) { - if (oldItem.lesson == newItem.lesson && oldItem.timeLeft != newItem.timeLeft) { - "time_left" - } else super.getChangePayload(oldItem, newItem) - } else super.getChangePayload(oldItem, newItem) - } - } + override fun getNewListSize() = newList.size + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition].id == newList[newItemPosition].id + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition] == newList[newItemPosition] && !isFlagsDifferent } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt index e8a85347..3f8622f8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt @@ -1,24 +1,27 @@ package io.github.wulkanowy.ui.modules.timetable import android.annotation.SuppressLint -import android.app.Dialog import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG import android.os.Bundle +import android.view.LayoutInflater import android.view.View import android.view.View.GONE import android.view.View.VISIBLE -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.databinding.DialogTimetableBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.* -import java.time.Instant +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.decapitalise +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.lifecycleAwareVariable +import io.github.wulkanowy.utils.toFormattedString +import java.time.LocalDateTime -@AndroidEntryPoint -class TimetableDialog : BaseDialogFragment() { +class TimetableDialog : DialogFragment() { + + private var binding: DialogTimetableBinding by lifecycleAwareVariable() private lateinit var lesson: Timetable @@ -26,27 +29,30 @@ class TimetableDialog : BaseDialogFragment() { private const val ARGUMENT_KEY = "Item" - fun newInstance(lesson: Timetable) = TimetableDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to lesson) + fun newInstance(exam: Timetable) = TimetableDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - lesson = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + lesson = getSerializable(ARGUMENT_KEY) as Timetable + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogTimetableBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogTimetableBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) with(lesson) { - setInfo(info, canceled, changes) + setInfo(info, teacher, canceled, changes) setSubject(subject, subjectOld) setTeacher(teacher, teacherOld) setGroup(group) @@ -74,36 +80,30 @@ class TimetableDialog : BaseDialogFragment() { } @SuppressLint("DefaultLocale") - private fun setInfo(info: String, canceled: Boolean, changes: Boolean) { + private fun setInfo(info: String, teacher: String, canceled: Boolean, changes: Boolean) { with(binding) { when { info.isNotBlank() -> { if (canceled) { timetableDialogChangesTitle.setTextColor( requireContext().getThemeAttrColor( - R.attr.colorTimetableCanceled - ) - ) - timetableDialogChangesValue.setTextColor( - requireContext().getThemeAttrColor( - R.attr.colorTimetableCanceled + R.attr.colorPrimary ) ) + timetableDialogChangesValue.setTextColor(requireContext().getThemeAttrColor(R.attr.colorPrimary)) } else { timetableDialogChangesTitle.setTextColor( requireContext().getThemeAttrColor( R.attr.colorTimetableChange ) ) - timetableDialogChangesValue.setTextColor( - requireContext().getThemeAttrColor( - R.attr.colorTimetableChange - ) - ) + timetableDialogChangesValue.setTextColor(requireContext().getThemeAttrColor(R.attr.colorTimetableChange)) } timetableDialogChangesValue.text = when { canceled && !changes -> "Lekcja odwołana: $info" + changes && teacher.isNotBlank() -> "Zastępstwo: $teacher" + changes && teacher.isBlank() -> "Zastępstwo, ${info.decapitalise()}" else -> info.capitalise() } } @@ -131,15 +131,6 @@ class TimetableDialog : BaseDialogFragment() { } } } - teacherOld.isNotBlank() && teacherOld == teacher -> { - timetableDialogTeacherValue.run { - visibility = GONE - } - timetableDialogTeacherNewValue.run { - visibility = VISIBLE - text = teacher - } - } teacher.isNotBlank() -> timetableDialogTeacherValue.text = teacher else -> { timetableDialogTeacherTitle.visibility = GONE @@ -187,7 +178,7 @@ class TimetableDialog : BaseDialogFragment() { } @SuppressLint("SetTextI18n") - private fun setTime(start: Instant, end: Instant) { + private fun setTime(start: LocalDateTime, end: LocalDateTime) { binding.timetableDialogTimeValue.text = "${start.toFormattedString("HH:mm")} - ${end.toFormattedString("HH:mm")}" } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt index ebc16239..a65d6921 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt @@ -7,9 +7,10 @@ import android.view.MenuItem import android.view.View import android.view.View.GONE import android.view.View.VISIBLE -import androidx.core.os.bundleOf -import androidx.core.text.parseAsHtml +import androidx.core.text.HtmlCompat import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.datepicker.CalendarConstraints +import com.google.android.material.datepicker.MaterialDatePicker import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Timetable @@ -20,7 +21,13 @@ import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.timetable.additional.AdditionalLessonsFragment import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment import io.github.wulkanowy.ui.widgets.DividerItemDecoration -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.SchoolDaysValidator +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.schoolYearEnd +import io.github.wulkanowy.utils.schoolYearStart +import io.github.wulkanowy.utils.toLocalDateTime +import io.github.wulkanowy.utils.toTimestamp import java.time.LocalDate import javax.inject.Inject @@ -37,20 +44,15 @@ class TimetableFragment : BaseFragment(R.layout.fragme companion object { private const val SAVED_DATE_KEY = "CURRENT_DATE" - private const val ARGUMENT_DATE_KEY = "ARGUMENT_DATE" - - fun newInstance(date: LocalDate? = null) = TimetableFragment().apply { - arguments = date?.let { bundleOf(ARGUMENT_DATE_KEY to it.toEpochDay()) } - } + fun newInstance() = TimetableFragment() } override val titleStringId get() = R.string.timetable_title - override val isViewEmpty get() = timetableAdapter.itemCount == 0 + override val isViewEmpty get() = timetableAdapter.itemCount > 0 override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -60,14 +62,12 @@ class TimetableFragment : BaseFragment(R.layout.fragme super.onViewCreated(view, savedInstanceState) binding = FragmentTimetableBinding.bind(view) messageContainer = binding.timetableRecycler - - val initDate = savedInstanceState?.getLong(SAVED_DATE_KEY) - ?: arguments?.getLong(ARGUMENT_DATE_KEY)?.takeUnless { it == 0L } - - presenter.onAttachView(this, initDate) + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) } override fun initView() { + timetableAdapter.onClickListener = presenter::onTimetableItemSelected + with(binding.timetableRecycler) { layoutManager = LinearLayoutManager(context) adapter = timetableAdapter @@ -87,7 +87,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme timetableNavDate.setOnClickListener { presenter.onPickDate() } timetableNextButton.setOnClickListener { presenter.onNextDay() } - timetableNavContainer.elevation = requireContext().dpToPx(3f) + timetableNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -103,8 +103,18 @@ class TimetableFragment : BaseFragment(R.layout.fragme } } - override fun updateData(data: List) { - timetableAdapter.submitList(data) + override fun updateData( + data: List, + showWholeClassPlanType: String, + showGroupsInPlanType: Boolean, + showTimetableTimers: Boolean + ) { + timetableAdapter.submitList( + newTimetable = data.toMutableList(), + showGroupsInPlan = showGroupsInPlanType, + showTimers = showTimetableTimers, + showWholeClassPlan = showWholeClassPlanType + ) } override fun clearData() { @@ -137,7 +147,9 @@ class TimetableFragment : BaseFragment(R.layout.fragme override fun setDayHeaderMessage(message: String?) { binding.timetableEmptyMessage.visibility = if (message.isNullOrEmpty()) GONE else VISIBLE - binding.timetableEmptyMessage.text = message.orEmpty().parseAsHtml() + binding.timetableEmptyMessage.text = HtmlCompat.fromHtml( + message.orEmpty(), HtmlCompat.FROM_HTML_MODE_COMPACT + ) } override fun showErrorView(show: Boolean) { @@ -172,15 +184,27 @@ class TimetableFragment : BaseFragment(R.layout.fragme (activity as? MainActivity)?.showDialogFragment(TimetableDialog.newInstance(lesson)) } - override fun showDatePickerDialog(selectedDate: LocalDate) { - openMaterialDatePicker( - selected = selectedDate, - rangeStart = selectedDate.firstSchoolDayInSchoolYear, - rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear, - onDateSelected = { - presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth) - } - ) + override fun showDatePickerDialog(currentDate: LocalDate) { + val baseDate = currentDate.schoolYearStart + val rangeStart = baseDate.toTimestamp() + val rangeEnd = LocalDate.now().schoolYearEnd.toTimestamp() + + val constraintsBuilder = CalendarConstraints.Builder().apply { + setValidator(SchoolDaysValidator(rangeStart, rangeEnd)) + setStart(rangeStart) + setEnd(rangeEnd) + } + val datePicker = MaterialDatePicker.Builder.datePicker() + .setCalendarConstraints(constraintsBuilder.build()) + .setSelection(currentDate.toTimestamp()) + .build() + + datePicker.addOnPositiveButtonClickListener { + val date = it.toLocalDateTime() + presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth) + } + + datePicker.show(this@TimetableFragment.parentFragmentManager, null) } override fun openAdditionalLessonsView() { @@ -197,6 +221,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme } override fun onDestroyView() { + timetableAdapter.clearTimers() presenter.onDetachView() super.onDestroyView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt deleted file mode 100644 index 92716ace..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable - -import io.github.wulkanowy.data.db.entities.Timetable -import java.time.Duration - -sealed class TimetableItem(val type: TimetableItemType) { - - data class Small( - val lesson: Timetable, - val onClick: (Timetable) -> Unit, - ) : TimetableItem(TimetableItemType.SMALL) - - data class Normal( - val lesson: Timetable, - val showGroupsInPlan: Boolean, - val timeLeft: TimeLeft?, - val onClick: (Timetable) -> Unit, - ) : TimetableItem(TimetableItemType.NORMAL) -} - -data class TimeLeft( - val until: Duration?, - val left: Duration?, - val isJustFinished: Boolean, -) - -enum class TimetableItemType { - SMALL, - NORMAL, -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt index d0687408..fa1bbfb2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -1,25 +1,33 @@ package io.github.wulkanowy.ui.modules.timetable -import io.github.wulkanowy.data.* +import android.annotation.SuppressLint +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.data.enums.TimetableMode 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.* +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.flowWithResourceIn +import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.nextSchoolDay +import io.github.wulkanowy.utils.previousSchoolDay +import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEach import timber.log.Timber -import java.time.Instant import java.time.LocalDate -import java.time.LocalDate.* -import java.util.* +import java.time.LocalDate.now +import java.time.LocalDate.of +import java.time.LocalDate.ofEpochDay import javax.inject.Inject -import kotlin.concurrent.timer class TimetablePresenter @Inject constructor( errorHandler: ErrorHandler, @@ -37,8 +45,6 @@ class TimetablePresenter @Inject constructor( private lateinit var lastError: Throwable - private var tickTimer: Timer? = null - fun onAttachView(view: TimetableView, date: Long?) { super.onAttachView(view) view.initView() @@ -87,22 +93,23 @@ class TimetablePresenter @Inject constructor( fun onViewReselected() { Timber.i("Timetable view is reselected") - view?.let { view -> + view?.also { view -> if (view.currentStackSize == 1) { - baseDate = now().nextOrSameSchoolDay - - if (currentDate != baseDate) { - reloadView(baseDate) - loadData() - } else if (!view.isViewEmpty) { - view.resetView() + baseDate.also { + if (currentDate != it) { + reloadView(it) + loadData() + } else if (!view.isViewEmpty) view.resetView() } - } else { - view.popView() - } + } else view.popView() } } + fun onTimetableItemSelected(lesson: Timetable) { + Timber.i("Select timetable item ${lesson.id}") + view?.showTimetableDialog(lesson) + } + fun onAdditionalLessonsSwitchSelected(): Boolean { view?.openAdditionalLessonsView() return true @@ -127,106 +134,70 @@ class TimetablePresenter @Inject constructor( } private fun loadData(forceRefresh: Boolean = false) { - flatResourceFlow { + Timber.i("Loading timetable data started") + + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) timetableRepository.getTimetable( - student = student, - semester = semester, - start = currentDate, - end = currentDate, - forceRefresh = forceRefresh, - timetableType = TimetableRepository.TimetableType.NORMAL + student, semester, currentDate, currentDate, forceRefresh ) - } - .logResourceStatus("load timetable data") - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showContent(it.lessons.isNotEmpty()) - showEmpty(it.lessons.isEmpty()) - updateData(it.lessons) - setDayHeaderMessage(it.headers.singleOrNull { header -> header.date == currentDate }?.content) + }.onEach { + when (it.status) { + Status.LOADING -> { + if (!it.data?.lessons.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data!!.lessons) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading timetable result: Success") + view?.apply { + updateData(it.data!!.lessons) + showEmpty(it.data.lessons.isEmpty()) + setDayHeaderMessage(it.data.headers.singleOrNull { header -> + header.date == currentDate + }?.content) + showErrorView(false) + showContent(it.data.lessons.isNotEmpty()) + } + analytics.logEvent( + "load_data", + "type" to "timetable", + "items" to it.data!!.lessons.size + ) + } + Status.ERROR -> { + Timber.i("Loading timetable result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "timetable", - "items" to it.lessons.size - ) + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showProgress(false) - showRefresh(false) - } - } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun updateData(lessons: List) { - tickTimer?.cancel() - - if (!prefRepository.showTimetableTimers) { - view?.updateData(createItems(lessons)) - } else { - tickTimer = timer(period = 2_000) { - view?.updateData(createItems(lessons)) - } - } - } - - private fun createItems(items: List): List { - val filteredItems = items - .filter { - if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) { - it.isStudentPlan - } else true - }.sortedWith( - compareBy({ item -> item.number }, { item -> !item.isStudentPlan }) - ) - - return filteredItems.mapIndexed { i, it -> - if (it.isStudentPlan) TimetableItem.Normal( - lesson = it, - showGroupsInPlan = prefRepository.showGroupsInPlan, - timeLeft = filteredItems.getTimeLeftForLesson(it, i), - onClick = ::onTimetableItemSelected - ) else TimetableItem.Small( - lesson = it, - onClick = ::onTimetableItemSelected - ) - } - } - - private fun List.getTimeLeftForLesson(lesson: Timetable, index: Int): TimeLeft { - val isShowTimeUntil = lesson.isShowTimeUntil(getPreviousLesson(index)) - return TimeLeft( - until = lesson.until.plusMinutes(1).takeIf { isShowTimeUntil }, - left = lesson.left?.plusMinutes(1), - isJustFinished = lesson.isJustFinished, + view?.updateData( + showWholeClassPlanType = prefRepository.showWholeClassPlan, + showGroupsInPlanType = prefRepository.showGroupsInPlan, + showTimetableTimers = prefRepository.showTimetableTimers, + data = createItems(lessons) ) } - private fun List.getPreviousLesson(position: Int): Instant? { - return filter { it.isStudentPlan } - .getOrNull(position - 1 - filterIndexed { i, item -> i < position && !item.isStudentPlan }.size) - ?.let { - if (!it.canceled && it.isStudentPlan) it.end - else null - } - } - - private fun onTimetableItemSelected(lesson: Timetable) { - Timber.i("Select timetable item ${lesson.id}") - view?.showTimetableDialog(lesson) - } + private fun createItems(items: List) = items.filter { item -> + if (prefRepository.showWholeClassPlan == "no") item.isStudentPlan else true + }.sortedWith(compareBy({ item -> item.number }, { item -> !item.isStudentPlan })) private fun showErrorViewOnError(message: String, error: Throwable) { view?.run { @@ -254,6 +225,7 @@ class TimetablePresenter @Inject constructor( } } + @SuppressLint("DefaultLocale") private fun reloadNavigation() { view?.apply { showPreButton(!currentDate.minusDays(1).isHolidays) @@ -261,10 +233,4 @@ class TimetablePresenter @Inject constructor( updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise()) } } - - override fun onDetachView() { - tickTimer?.cancel() - tickTimer = null - super.onDetachView() - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt index 8cfb2620..4afb0b05 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt @@ -12,7 +12,7 @@ interface TimetableView : BaseView { fun initView() - fun updateData(data: List) + fun updateData(data: List, showWholeClassPlanType: String, showGroupsInPlanType: Boolean, showTimetableTimers: Boolean) fun updateNavigationDay(date: String) @@ -42,7 +42,7 @@ interface TimetableView : BaseView { fun showTimetableDialog(lesson: Timetable) - fun showDatePickerDialog(selectedDate: LocalDate) + fun showDatePickerDialog(currentDate: LocalDate) fun popView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt index c2ce8028..fdc8b887 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt @@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.timetable.additional import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.ViewGroup -import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.data.db.entities.TimetableAdditional import io.github.wulkanowy.databinding.ItemTimetableAdditionalBinding @@ -15,8 +14,6 @@ class AdditionalLessonsAdapter @Inject constructor() : var items = emptyList() - var onDeleteClickListener: (timetableAdditional: TimetableAdditional) -> Unit = {} - override fun getItemCount() = items.size override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( @@ -28,12 +25,8 @@ class AdditionalLessonsAdapter @Inject constructor() : val item = items[position] with(holder.binding) { - additionalLessonItemTime.text = - "${item.start.toFormattedString("HH:mm")} - ${item.end.toFormattedString("HH:mm")}" + additionalLessonItemTime.text = "${item.start.toFormattedString("HH:mm")} - ${item.end.toFormattedString("HH:mm")}" additionalLessonItemSubject.text = item.subject - - additionalLessonItemDelete.isVisible = item.isAddedByUser - additionalLessonItemDelete.setOnClickListener { onDeleteClickListener(item) } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt index faa833c2..a4e1f0fc 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt @@ -3,17 +3,22 @@ package io.github.wulkanowy.ui.modules.timetable.additional import android.os.Bundle import android.view.View import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.datepicker.CalendarConstraints +import com.google.android.material.datepicker.MaterialDatePicker import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.TimetableAdditional import io.github.wulkanowy.databinding.FragmentTimetableAdditionalBinding import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.ui.modules.timetable.additional.add.AdditionalLessonAddDialog import io.github.wulkanowy.ui.widgets.DividerItemDecoration -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.SchoolDaysValidator +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.schoolYearEnd +import io.github.wulkanowy.utils.schoolYearStart +import io.github.wulkanowy.utils.toLocalDateTime +import io.github.wulkanowy.utils.toTimestamp import java.time.LocalDate import javax.inject.Inject @@ -48,9 +53,7 @@ class AdditionalLessonsFragment : override fun initView() { with(binding.additionalLessonsRecycler) { layoutManager = LinearLayoutManager(context) - adapter = additionalLessonsAdapter.apply { - onDeleteClickListener = { presenter.onDeleteLessonsSelected(it) } - } + adapter = additionalLessonsAdapter addItemDecoration(DividerItemDecoration(context)) } @@ -58,7 +61,9 @@ class AdditionalLessonsFragment : additionalLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) additionalLessonsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) additionalLessonsSwipe.setProgressBackgroundColorSchemeColor( - requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + requireContext().getThemeAttrColor( + R.attr.colorSwipeRefresh + ) ) additionalLessonsErrorRetry.setOnClickListener { presenter.onRetry() } additionalLessonsErrorDetails.setOnClickListener { presenter.onDetailsClick() } @@ -67,9 +72,7 @@ class AdditionalLessonsFragment : additionalLessonsNavDate.setOnClickListener { presenter.onPickDate() } additionalLessonsNextButton.setOnClickListener { presenter.onNextDay() } - openAddAdditionalLessonButton.setOnClickListener { presenter.onAdditionalLessonAddButtonClicked() } - - additionalLessonsNavContainer.elevation = requireContext().dpToPx(3f) + additionalLessonsNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -87,10 +90,6 @@ class AdditionalLessonsFragment : } } - override fun showSuccessMessage() { - getString(R.string.additional_lessons_delete_success) - } - override fun updateNavigationDay(date: String) { binding.additionalLessonsNavDate.text = date } @@ -132,33 +131,28 @@ class AdditionalLessonsFragment : binding.additionalLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE } - override fun showAddAdditionalLessonDialog() { - (activity as? MainActivity)?.showDialogFragment(AdditionalLessonAddDialog.newInstance()) - } - - override fun showDatePickerDialog(selectedDate: LocalDate) { + override fun showDatePickerDialog(currentDate: LocalDate) { val now = LocalDate.now() + val startOfSchoolYear = now.schoolYearStart.toTimestamp() + val endOfSchoolYear = now.schoolYearEnd.toTimestamp() - openMaterialDatePicker( - selected = selectedDate, - rangeStart = now.firstSchoolDayInSchoolYear, - rangeEnd = now.lastSchoolDayInSchoolYear, - onDateSelected = { - presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth) - } - ) - } + val constraintsBuilder = CalendarConstraints.Builder().apply { + setValidator(SchoolDaysValidator(startOfSchoolYear, endOfSchoolYear)) + setStart(startOfSchoolYear) + setEnd(endOfSchoolYear) + } + val datePicker = + MaterialDatePicker.Builder.datePicker() + .setCalendarConstraints(constraintsBuilder.build()) + .setSelection(currentDate.toTimestamp()) + .build() - override fun showDeleteLessonDialog(timetableAdditional: TimetableAdditional) { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.additional_lessons_delete_title)) - .setItems( - arrayOf( - getString(R.string.additional_lessons_delete_one), - getString(R.string.additional_lessons_delete_series) - ) - ) { _, position -> presenter.onDeleteDialogSelectItem(position, timetableAdditional) } - .show() + datePicker.addOnPositiveButtonClickListener { + val date = it.toLocalDateTime() + presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth) + } + + datePicker.show(this@AdditionalLessonsFragment.parentFragmentManager, null) } override fun onSaveInstanceState(outState: Bundle) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt index d0a01b38..3496e141 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt @@ -1,18 +1,25 @@ package io.github.wulkanowy.ui.modules.timetable.additional import android.annotation.SuppressLint -import io.github.wulkanowy.data.* -import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.data.Status 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.* +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.flowWithResourceIn +import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.nextSchoolDay +import io.github.wulkanowy.utils.previousSchoolDay +import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch import timber.log.Timber import java.time.LocalDate import javax.inject.Inject @@ -56,10 +63,6 @@ class AdditionalLessonsPresenter @Inject constructor( view?.showDatePickerDialog(currentDate) } - fun onAdditionalLessonAddButtonClicked() { - view?.showAddAdditionalLessonDialog() - } - fun onDateSet(year: Int, month: Int, day: Int) { loadData(LocalDate.of(year, month, day)) reloadView() @@ -95,77 +98,42 @@ class AdditionalLessonsPresenter @Inject constructor( }.launch("holidays") } - fun onDeleteLessonsSelected(timetableAdditional: TimetableAdditional) { - if (timetableAdditional.repeatId == null) { - deleteAdditionalLessons(timetableAdditional, false) - } else { - view?.showDeleteLessonDialog(timetableAdditional) - } - } - - fun onDeleteDialogSelectItem(position: Int, timetableAdditional: TimetableAdditional) { - deleteAdditionalLessons(timetableAdditional, position == 1) - } - - private fun deleteAdditionalLessons( - timetableAdditional: TimetableAdditional, - deleteSeries: Boolean - ) { - presenterScope.launch { - Timber.i("Additional Lesson delete start") - runCatching { timetableRepository.deleteAdditional(timetableAdditional, deleteSeries) } - .onSuccess { - Timber.i("Additional Lesson delete: Success") - view?.showSuccessMessage() - } - .onFailure { - Timber.i("Additional Lesson delete result: An exception occurred") - errorHandler.dispatch(it) - } - } - } - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { currentDate = date - flatResourceFlow { + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) - timetableRepository.getTimetable( - student = student, - semester = semester, - start = date, - end = date, - forceRefresh = forceRefresh, - refreshAdditional = true, - timetableType = TimetableRepository.TimetableType.ADDITIONAL - ) - } - .logResourceStatus("load additional lessons") - .onResourceData { - view?.apply { - updateData(it.additional.sortedBy { item -> item.start }) - showEmpty(it.additional.isEmpty()) - showErrorView(false) - showContent(it.additional.isNotEmpty()) + timetableRepository.getTimetable(student, semester, date, date, forceRefresh, true) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading additional lessons data started") + Status.SUCCESS -> { + Timber.i("Loading additional lessons lessons result: Success") + view?.apply { + updateData(it.data!!.additional.sortedBy { item -> item.date }) + showEmpty(it.data.additional.isEmpty()) + showErrorView(false) + showContent(it.data.additional.isNotEmpty()) + } + analytics.logEvent( + "load_data", + "type" to "additional_lessons", + "items" to it.data!!.additional.size + ) + } + Status.ERROR -> { + Timber.i("Loading additional lessons result: An exception occurred") + errorHandler.dispatch(it.error!!) } } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "additional_lessons", - "items" to it.additional.size - ) + }.afterLoading { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) } - .onResourceNotLoading { - view?.run { - hideRefresh() - showProgress(false) - enableSwipe(true) - } - } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt index 76d37b75..97eb2ae7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt @@ -34,11 +34,5 @@ interface AdditionalLessonsView : BaseView { fun showNextButton(show: Boolean) - fun showDatePickerDialog(selectedDate: LocalDate) - - fun showAddAdditionalLessonDialog() - - fun showSuccessMessage() - - fun showDeleteLessonDialog(timetableAdditional: TimetableAdditional) + fun showDatePickerDialog(currentDate: LocalDate) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt deleted file mode 100644 index 13471997..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt +++ /dev/null @@ -1,170 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.additional.add - -import android.app.Dialog -import android.os.Bundle -import android.view.View -import androidx.core.widget.doOnTextChanged -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.timepicker.MaterialTimePicker -import com.google.android.material.timepicker.TimeFormat -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.databinding.DialogAdditionalAddBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear -import io.github.wulkanowy.utils.openMaterialDatePicker -import io.github.wulkanowy.utils.toFormattedString -import java.time.LocalDate -import java.time.LocalTime -import javax.inject.Inject - -@AndroidEntryPoint -class AdditionalLessonAddDialog : BaseDialogFragment(), - AdditionalLessonAddView { - - @Inject - lateinit var presenter: AdditionalLessonAddPresenter - - companion object { - fun newInstance() = AdditionalLessonAddDialog() - } - - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView( - DialogAdditionalAddBinding.inflate(layoutInflater).apply { binding = this }.root - ) - .create() - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - presenter.onAttachView(this) - } - - override fun initView() { - with(binding) { - additionalLessonDialogStartEdit.doOnTextChanged { _, _, _, _ -> - additionalLessonDialogStart.isErrorEnabled = false - additionalLessonDialogStart.error = null - } - additionalLessonDialogEndEdit.doOnTextChanged { _, _, _, _ -> - additionalLessonDialogEnd.isErrorEnabled = false - additionalLessonDialogEnd.error = null - } - additionalLessonDialogDateEdit.doOnTextChanged { _, _, _, _ -> - additionalLessonDialogDate.isErrorEnabled = false - additionalLessonDialogDate.error = null - } - additionalLessonDialogContentEdit.doOnTextChanged { _, _, _, _ -> - additionalLessonDialogContent.isErrorEnabled = false - additionalLessonDialogContent.error = null - } - - additionalLessonDialogAdd.setOnClickListener { - presenter.onAddAdditionalClicked( - start = additionalLessonDialogStartEdit.text?.toString(), - end = additionalLessonDialogEndEdit.text?.toString(), - date = additionalLessonDialogDateEdit.text?.toString(), - content = additionalLessonDialogContentEdit.text?.toString(), - isRepeat = additionalLessonDialogRepeat.isChecked - ) - } - additionalLessonDialogClose.setOnClickListener { dismiss() } - additionalLessonDialogDateEdit.setOnClickListener { presenter.showDatePicker() } - additionalLessonDialogStartEdit.setOnClickListener { presenter.showStartTimePicker() } - additionalLessonDialogEndEdit.setOnClickListener { presenter.showEndTimePicker() } - } - } - - override fun showSuccessMessage() { - showMessage(getString(R.string.additional_lessons_add_success)) - } - - override fun setErrorDateRequired() { - with(binding.additionalLessonDialogDate) { - isErrorEnabled = true - error = getString(R.string.error_field_required) - } - } - - override fun setErrorStartRequired() { - with(binding.additionalLessonDialogStart) { - isErrorEnabled = true - error = getString(R.string.error_field_required) - } - } - - override fun setErrorEndRequired() { - with(binding.additionalLessonDialogEnd) { - isErrorEnabled = true - error = getString(R.string.error_field_required) - } - } - - override fun setErrorContentRequired() { - with(binding.additionalLessonDialogContent) { - isErrorEnabled = true - error = getString(R.string.error_field_required) - } - } - - override fun setErrorIncorrectEndTime() { - with(binding.additionalLessonDialogEnd) { - isErrorEnabled = true - error = getString(R.string.additional_lessons_end_time_error) - } - } - - override fun closeDialog() { - dismiss() - } - - override fun showDatePickerDialog(selectedDate: LocalDate) { - openMaterialDatePicker( - selected = selectedDate, - rangeStart = LocalDate.now(), - rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear, - onDateSelected = { - presenter.onDateSelected(it) - binding.additionalLessonDialogDateEdit.setText(it.toFormattedString()) - } - ) - } - - override fun showStartTimePickerDialog(selectedTime: LocalTime) { - showTimePickerDialog(selectedTime) { - presenter.onStartTimeSelected(it) - binding.additionalLessonDialogStartEdit.setText(it.toString()) - } - } - - override fun showEndTimePickerDialog(selectedTime: LocalTime) { - showTimePickerDialog(selectedTime) { - presenter.onEndTimeSelected(it) - binding.additionalLessonDialogEndEdit.setText(it.toString()) - } - } - - private fun showTimePickerDialog(defaultTime: LocalTime, onTimeSelected: (LocalTime) -> Unit) { - val timePicker = MaterialTimePicker.Builder() - .setTimeFormat(TimeFormat.CLOCK_24H) - .setHour(defaultTime.hour) - .setMinute(defaultTime.minute) - .build() - - timePicker.addOnPositiveButtonClickListener { - onTimeSelected(LocalTime.of(timePicker.hour, timePicker.minute)) - } - - if (!parentFragmentManager.isStateSaved) { - timePicker.show(parentFragmentManager, null) - } - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt deleted file mode 100644 index c207165d..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt +++ /dev/null @@ -1,164 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.additional.add - -import io.github.wulkanowy.data.db.entities.TimetableAdditional -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.lastSchoolDayInSchoolYear -import io.github.wulkanowy.utils.toLocalDate -import kotlinx.coroutines.launch -import timber.log.Timber -import java.time.* -import java.time.temporal.ChronoUnit -import java.util.* -import javax.inject.Inject - -class AdditionalLessonAddPresenter @Inject constructor( - errorHandler: ErrorHandler, - studentRepository: StudentRepository, - private val timetableRepository: TimetableRepository, - private val semesterRepository: SemesterRepository -) : BasePresenter(errorHandler, studentRepository) { - - private var selectedStartTime = LocalTime.of(15, 0) - - private var selectedEndTime = LocalTime.of(15, 45) - - private var selectedDate = LocalDate.now() - - override fun onAttachView(view: AdditionalLessonAddView) { - super.onAttachView(view) - view.initView() - Timber.i("AdditionalLesson details view was initialized") - } - - fun showDatePicker() { - view?.showDatePickerDialog(selectedDate) - } - - fun showStartTimePicker() { - view?.showStartTimePickerDialog(selectedStartTime) - } - - fun showEndTimePicker() { - view?.showEndTimePickerDialog(selectedEndTime) - } - - fun onStartTimeSelected(time: LocalTime) { - selectedStartTime = time - } - - fun onEndTimeSelected(time: LocalTime) { - selectedEndTime = time - } - - fun onDateSelected(date: LocalDate) { - selectedDate = date - } - - fun onAddAdditionalClicked( - start: String?, - end: String?, - date: String?, - content: String?, - isRepeat: Boolean - ) { - if (isUserInputValid(start, end, date, content)) { - addAdditionalLesson( - start = LocalTime.parse(start!!), - end = LocalTime.parse(end), - date = date!!.toLocalDate(), - subject = content!!, - isRepeat = isRepeat - ) - } - } - - private fun isUserInputValid( - start: String?, - end: String?, - date: String?, - content: String? - ): Boolean { - var isValid = true - - if (start.isNullOrBlank()) { - view?.setErrorStartRequired() - isValid = false - } - - if (end.isNullOrBlank()) { - view?.setErrorEndRequired() - isValid = false - } - - if (date.isNullOrBlank()) { - view?.setErrorDateRequired() - isValid = false - } - - if (content.isNullOrBlank()) { - view?.setErrorContentRequired() - isValid = false - } - - if (selectedStartTime >= selectedEndTime) { - view?.setErrorIncorrectEndTime() - isValid = false - } - - return isValid - } - - private fun addAdditionalLesson( - start: LocalTime, - end: LocalTime, - date: LocalDate, - subject: String, - isRepeat: Boolean - ) { - presenterScope.launch { - val semester = runCatching { - val student = studentRepository.getCurrentStudent() - semesterRepository.getCurrentSemester(student) - } - .onFailure(errorHandler::dispatch) - .getOrNull() ?: return@launch - - val weeks = if (isRepeat) { - ChronoUnit.WEEKS.between(date, date.lastSchoolDayInSchoolYear) - } else 0 - val uniqueRepeatId = UUID.randomUUID().takeIf { isRepeat } - - val lessonsToAdd = (0..weeks).map { - TimetableAdditional( - studentId = semester.studentId, - diaryId = semester.diaryId, - start = ZonedDateTime.of(date, start, ZoneId.systemDefault()).toInstant(), - end = ZonedDateTime.of(date, end, ZoneId.systemDefault()).toInstant(), - date = date.plusWeeks(it), - subject = subject - ).apply { - isAddedByUser = true - repeatId = uniqueRepeatId - } - } - - Timber.i("AdditionalLesson insert start") - runCatching { timetableRepository.saveAdditionalList(lessonsToAdd) } - .onSuccess { - Timber.i("AdditionalLesson insert: Success") - view?.run { - showSuccessMessage() - closeDialog() - } - } - .onFailure { - Timber.i("AdditionalLesson insert result: An exception occurred") - errorHandler.dispatch(it) - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddView.kt deleted file mode 100644 index 0df53815..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddView.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetable.additional.add - -import io.github.wulkanowy.ui.base.BaseView -import java.time.LocalDate -import java.time.LocalTime - -interface AdditionalLessonAddView : BaseView { - - fun initView() - - fun closeDialog() - - fun showDatePickerDialog(selectedDate: LocalDate) - - fun showStartTimePickerDialog(selectedTime: LocalTime) - - fun showEndTimePickerDialog(selectedTime: LocalTime) - - fun showSuccessMessage() - - fun setErrorDateRequired() - - fun setErrorStartRequired() - - fun setErrorEndRequired() - - fun setErrorContentRequired() - - fun setErrorIncorrectEndTime() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt index d937d4dd..7d32278f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt @@ -1,18 +1,17 @@ package io.github.wulkanowy.ui.modules.timetable.completed -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.databinding.DialogLessonCompletedBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.serializable +import io.github.wulkanowy.utils.lifecycleAwareVariable -@AndroidEntryPoint -class CompletedLessonDialog : BaseDialogFragment() { +class CompletedLessonDialog : DialogFragment() { + + private var binding: DialogLessonCompletedBinding by lifecycleAwareVariable() private lateinit var completedLesson: CompletedLesson @@ -20,23 +19,24 @@ class CompletedLessonDialog : BaseDialogFragment() private const val ARGUMENT_KEY = "Item" - fun newInstance(lesson: CompletedLesson) = CompletedLessonDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to lesson) + fun newInstance(exam: CompletedLesson) = CompletedLessonDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - completedLesson = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + completedLesson = getSerializable(ARGUMENT_KEY) as CompletedLesson + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView( - DialogLessonCompletedBinding.inflate(layoutInflater).apply { binding = this }.root - ) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogLessonCompletedBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt index 36e38fb9..00ba0bad 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt @@ -1,13 +1,11 @@ package io.github.wulkanowy.ui.modules.timetable.completed -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext +import android.content.res.Resources import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject -class CompletedLessonsErrorHandler @Inject constructor(@ApplicationContext context: Context) : - ErrorHandler(context) { +class CompletedLessonsErrorHandler @Inject constructor(resources: Resources) : ErrorHandler(resources) { var onFeatureDisabled: () -> Unit = {} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt index 77a7bbd5..ad698c1c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt @@ -2,8 +2,12 @@ package io.github.wulkanowy.ui.modules.timetable.completed import android.os.Bundle import android.view.View -import android.view.View.* +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.datepicker.CalendarConstraints +import com.google.android.material.datepicker.MaterialDatePicker import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.CompletedLesson @@ -12,7 +16,14 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.widgets.DividerItemDecoration -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.SchoolDaysValidator +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.getCompatDrawable +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.schoolYearEnd +import io.github.wulkanowy.utils.schoolYearStart +import io.github.wulkanowy.utils.toLocalDateTime +import io.github.wulkanowy.utils.toTimestamp import java.time.LocalDate import javax.inject.Inject @@ -57,7 +68,9 @@ class CompletedLessonsFragment : completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) completedLessonsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) completedLessonsSwipe.setProgressBackgroundColorSchemeColor( - requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh) + requireContext().getThemeAttrColor( + R.attr.colorSwipeRefresh + ) ) completedLessonErrorRetry.setOnClickListener { presenter.onRetry() } completedLessonErrorDetails.setOnClickListener { presenter.onDetailsClick() } @@ -66,7 +79,7 @@ class CompletedLessonsFragment : completedLessonsNavDate.setOnClickListener { presenter.onPickDate() } completedLessonsNextButton.setOnClickListener { presenter.onNextDay() } - completedLessonsNavContainer.elevation = requireContext().dpToPx(3f) + completedLessonsNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -139,17 +152,28 @@ class CompletedLessonsFragment : ) } - override fun showDatePickerDialog(selectedDate: LocalDate) { + override fun showDatePickerDialog(currentDate: LocalDate) { val now = LocalDate.now() + val startOfSchoolYear = now.schoolYearStart.toTimestamp() + val endOfSchoolYear = now.schoolYearEnd.toTimestamp() - openMaterialDatePicker( - selected = selectedDate, - rangeStart = now.firstSchoolDayInSchoolYear, - rangeEnd = now.lastSchoolDayInSchoolYear, - onDateSelected = { - presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth) - } - ) + val constraintsBuilder = CalendarConstraints.Builder().apply { + setValidator(SchoolDaysValidator(startOfSchoolYear, endOfSchoolYear)) + setStart(startOfSchoolYear) + setEnd(endOfSchoolYear) + } + val datePicker = + MaterialDatePicker.Builder.datePicker() + .setCalendarConstraints(constraintsBuilder.build()) + .setSelection(currentDate.toTimestamp()) + .build() + + datePicker.addOnPositiveButtonClickListener { + val date = it.toLocalDateTime() + presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth) + } + + datePicker.show(this@CompletedLessonsFragment.parentFragmentManager, null) } override fun onSaveInstanceState(outState: Bundle) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt index 16c51fd2..b75b42f8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt @@ -1,13 +1,22 @@ package io.github.wulkanowy.ui.modules.timetable.completed import android.annotation.SuppressLint -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.data.repositories.CompletedLessonsRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.flowWithResourceIn +import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.nextSchoolDay +import io.github.wulkanowy.utils.previousSchoolDay +import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEach @@ -102,46 +111,51 @@ class CompletedLessonsPresenter @Inject constructor( } private fun loadData(forceRefresh: Boolean = false) { - flatResourceFlow { + Timber.i("Loading completed lessons data started") + + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) - completedLessonsRepository.getCompletedLessons( - student = student, - semester = semester, - start = currentDate, - end = currentDate, - forceRefresh = forceRefresh - ) - } - .logResourceStatus("load completed lessons") - .mapResourceData { it.sortedBy { lesson -> lesson.number } } - .onResourceData { - view?.run { - enableSwipe(true) - showProgress(false) - showErrorView(false) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) - updateData(it) + completedLessonsRepository.getCompletedLessons(student, semester, currentDate, currentDate, forceRefresh) + }.onEach { + when (it.status) { + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data.sortedBy { item -> item.number }) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading completed lessons lessons result: Success") + view?.apply { + updateData(it.data!!.sortedBy { item -> item.number }) + showEmpty(it.data.isEmpty()) + showErrorView(false) + showContent(it.data.isNotEmpty()) + } + analytics.logEvent( + "load_data", + "type" to "completed_lessons", + "items" to it.data!!.size + ) + } + Status.ERROR -> { + Timber.i("Loading completed lessons result: An exception occurred") + completedLessonsErrorHandler.dispatch(it.error!!) } } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceSuccess { - analytics.logEvent( - "load_data", - "type" to "completed_lessons", - "items" to it.size - ) + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showProgress(false) - showRefresh(false) - } - } - .onResourceError(errorHandler::dispatch) - .launch() + }.launch() } private fun showErrorViewOnError(message: String, error: Throwable) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt index 715ce01f..7a98874e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt @@ -38,5 +38,5 @@ interface CompletedLessonsView : BaseView { fun showCompletedLessonDialog(completedLesson: CompletedLesson) - fun showDatePickerDialog(selectedDate: LocalDate) + fun showDatePickerDialog(currentDate: LocalDate) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt index 672dbe72..a27dba88 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt @@ -1,18 +1,22 @@ package io.github.wulkanowy.ui.modules.timetablewidget -import android.appwidget.AppWidgetManager.* +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS import android.content.Intent +import android.os.Build import android.os.Bundle import android.widget.Toast import android.widget.Toast.LENGTH_LONG +import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.ActivityWidgetConfigureBinding import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.WidgetConfigureAdapter import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_CONFIGURE import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER import io.github.wulkanowy.utils.AppInfo import javax.inject.Inject @@ -31,6 +35,8 @@ class TimetableWidgetConfigureActivity : @Inject lateinit var appInfo: AppInfo + private var dialog: AlertDialog? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setResult(RESULT_CANCELED) @@ -56,6 +62,23 @@ class TimetableWidgetConfigureActivity : configureAdapter.onClickListener = presenter::onItemSelect } + override fun showThemeDialog() { + var items = arrayOf( + getString(R.string.widget_timetable_theme_light), + getString(R.string.widget_timetable_theme_dark) + ) + + if (appInfo.systemVersion >= Build.VERSION_CODES.Q) items += getString(R.string.widget_timetable_theme_system) + + dialog = AlertDialog.Builder(this, R.style.WulkanowyTheme_WidgetAccountSwitcher) + .setTitle(R.string.widget_timetable_theme_title) + .setOnDismissListener { presenter.onDismissThemeView() } + .setSingleChoiceItems(items, -1) { _, which -> + presenter.onThemeSelect(which) + } + .show() + } + override fun updateData(data: List, selectedStudentId: Long) { with(configureAdapter) { selectedId = selectedStudentId @@ -69,7 +92,6 @@ class TimetableWidgetConfigureActivity : .apply { action = ACTION_APPWIDGET_UPDATE putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId)) - putExtra(EXTRA_FROM_CONFIGURE, true) }) } @@ -88,4 +110,9 @@ class TimetableWidgetConfigureActivity : override fun openLoginView() { startActivity(LoginActivity.getStartIntent(this)) } + + override fun onDestroy() { + super.onDestroy() + dialog?.dismiss() + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt index 87e89336..2a40c8e4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt @@ -1,13 +1,14 @@ package io.github.wulkanowy.ui.modules.timetablewidget -import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getThemeWidgetKey +import io.github.wulkanowy.utils.flowWithResource import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -38,24 +39,41 @@ class TimetableWidgetConfigurePresenter @Inject constructor( fun onItemSelect(student: Student) { selectedStudent = student + + if (isFromProvider) registerStudent(selectedStudent) + else view?.showThemeDialog() + } + + fun onThemeSelect(index: Int) { + appWidgetId?.let { + sharedPref.putLong(getThemeWidgetKey(it), index.toLong()) + } registerStudent(selectedStudent) } + fun onDismissThemeView() { + view?.finishView() + } + private fun loadData() { - resourceFlow { studentRepository.getSavedStudents(false) }.onEach { - when (it) { - is Resource.Loading -> Timber.d("Timetable widget configure students data load") - is Resource.Success -> { + flowWithResource { studentRepository.getSavedStudents(false) }.onEach { + when (it.status) { + Status.LOADING -> Timber.d("Timetable widget configure students data load") + Status.SUCCESS -> { val selectedStudentId = appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } ?: -1 + when { - it.data.isEmpty() -> view?.openLoginView() - it.data.size == 1 && !isFromProvider -> onItemSelect(it.data.single().student) + it.data!!.isEmpty() -> view?.openLoginView() + it.data.size == 1 && !isFromProvider -> { + selectedStudent = it.data.single().student + view?.showThemeDialog() + } else -> view?.updateData(it.data, selectedStudentId) } } - is Resource.Error -> errorHandler.dispatch(it.error) + Status.ERROR -> errorHandler.dispatch(it.error!!) } }.launch() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt index 7740b9bb..accdc28d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt @@ -11,6 +11,8 @@ interface TimetableWidgetConfigureView : BaseView { fun updateTimetableWidget(widgetId: Int) + fun showThemeDialog() + fun setSuccessResult(widgetId: Int) fun finishView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt index 9c5abe1c..45b79b50 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt @@ -1,39 +1,38 @@ package io.github.wulkanowy.ui.modules.timetablewidget +import android.annotation.SuppressLint import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID import android.content.Context import android.content.Intent -import android.content.res.Configuration import android.graphics.Paint.ANTI_ALIAS_FLAG import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG import android.view.View.GONE import android.view.View.VISIBLE +import android.widget.AdapterView.INVALID_POSITION import android.widget.RemoteViews import android.widget.RemoteViewsService import io.github.wulkanowy.R -import io.github.wulkanowy.data.dataOrNull import io.github.wulkanowy.data.db.SharedPrefProvider -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.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.data.toFirstResult +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getCurrentThemeWidgetKey import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey -import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getTodayLastLessonEndDateTimeWidgetKey import io.github.wulkanowy.utils.getCompatColor +import io.github.wulkanowy.utils.toFirstResult import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.runBlocking import timber.log.Timber -import java.time.Instant import java.time.LocalDate class TimetableWidgetFactory( private val timetableRepository: TimetableRepository, private val studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, + private val prefRepository: PreferencesRepository, private val sharedPref: SharedPrefProvider, private val context: Context, private val intent: Intent? @@ -41,22 +40,19 @@ class TimetableWidgetFactory( private var lessons = emptyList() - private var timetableCanceledColor: Int? = null + private var savedCurrentTheme: Long? = null + + private var primaryColor: Int? = null private var textColor: Int? = null private var timetableChangeColor: Int? = null - private var lastSyncInstant: Instant? = null - override fun getLoadingView() = null override fun hasStableIds() = true - override fun getCount() = when { - lessons.isEmpty() -> 0 - else -> lessons.size + 1 - } + override fun getCount() = lessons.size override fun getViewTypeCount() = 2 @@ -71,170 +67,152 @@ class TimetableWidgetFactory( val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0)) val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0) - runCatching { - runBlocking { - val student = getStudent(studentId) ?: return@runBlocking - val semester = semesterRepository.getCurrentSemester(student) - lessons = getLessons(student, semester, date) - lastSyncInstant = - timetableRepository.getLastRefreshTimestamp(semester, date, date) - if (date == LocalDate.now()) { - updateTodayLastLessonEnd(appWidgetId) - } - } - }.onFailure { - Timber.e(it, "An error has occurred in timetable widget factory") - } + updateTheme(appWidgetId) + lessons = getLessons(date, studentId) } } - private suspend fun getStudent(studentId: Long): Student? { - val students = studentRepository.getSavedStudents() - return students.singleOrNull { it.student.id == studentId }?.student + private fun updateTheme(appWidgetId: Int) { + savedCurrentTheme = sharedPref.getLong(getCurrentThemeWidgetKey(appWidgetId), 0) + + if (savedCurrentTheme == 0L) { + primaryColor = R.color.colorPrimary + textColor = android.R.color.black + timetableChangeColor = R.color.timetable_change_dark + } else { + primaryColor = R.color.colorPrimaryLight + textColor = android.R.color.white + timetableChangeColor = R.color.timetable_change_light + } } - private suspend fun getLessons( - student: Student, semester: Semester, date: LocalDate - ): List { - val timetable = timetableRepository.getTimetable(student, semester, date, date, false) - val lessons = timetable.toFirstResult().dataOrNull?.lessons.orEmpty() - return lessons.sortedBy { it.number } + private fun getItemLayout(lesson: Timetable): Int { + return when { + prefRepository.showWholeClassPlan == "small" && !lesson.isStudentPlan -> { + if (savedCurrentTheme == 0L) R.layout.item_widget_timetable_small + else R.layout.item_widget_timetable_small_dark + } + savedCurrentTheme == 1L -> R.layout.item_widget_timetable_dark + else -> R.layout.item_widget_timetable + } } - private fun updateTodayLastLessonEnd(appWidgetId: Int) { - val todayLastLessonEnd = lessons.maxOfOrNull { it.end } ?: return - val key = getTodayLastLessonEndDateTimeWidgetKey(appWidgetId) - sharedPref.putLong(key, todayLastLessonEnd.epochSecond, true) - } - - companion object { - const val TIME_FORMAT_STYLE = "HH:mm" + private fun getLessons(date: LocalDate, studentId: Long) = try { + runBlocking { + if (!studentRepository.isStudentSaved()) return@runBlocking emptyList() + + val students = studentRepository.getSavedStudents() + val student = students.singleOrNull { it.student.id == studentId }?.student + ?: return@runBlocking emptyList() + + val semester = semesterRepository.getCurrentSemester(student) + timetableRepository.getTimetable(student, semester, date, date, false) + .toFirstResult().data?.lessons.orEmpty() + .sortedWith(compareBy({ it.number }, { !it.isStudentPlan })) + .filter { if (prefRepository.showWholeClassPlan == "no") it.isStudentPlan else true } + } + } catch (e: Exception) { + Timber.e(e, "An error has occurred in timetable widget factory") + emptyList() } + @SuppressLint("DefaultLocale") override fun getViewAt(position: Int): RemoteViews? { - if (position == lessons.size) { - val synchronizationInstant = lastSyncInstant ?: Instant.MIN - val synchronizationText = getSynchronizationInfoText(synchronizationInstant) - return RemoteViews(context.packageName, R.layout.item_widget_timetable_footer).apply { - setTextViewText(R.id.timetableWidgetSynchronizationTime, synchronizationText) - } - } + if (position == INVALID_POSITION || lessons.getOrNull(position) == null) return null - val lesson = lessons.getOrNull(position) ?: return null - - val lessonStartTime = lesson.start.toFormattedString(TIME_FORMAT_STYLE) - val lessonEndTime = lesson.end.toFormattedString(TIME_FORMAT_STYLE) - val roomText = "${context.getString(R.string.timetable_room)} ${lesson.room}" - - val remoteViews = RemoteViews(context.packageName, R.layout.item_widget_timetable).apply { - setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString()) - setTextViewText(R.id.timetableWidgetItemTimeStart, lessonStartTime) - setTextViewText(R.id.timetableWidgetItemTimeFinish, lessonEndTime) + val lesson = lessons[position] + return RemoteViews(context.packageName, getItemLayout(lesson)).apply { setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject) - setTextViewText(R.id.timetableWidgetItemRoom, roomText) - setTextViewText(R.id.timetableWidgetItemTeacher, lesson.teacher) - setTextViewText(R.id.timetableWidgetItemDescription, lesson.info) + setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString()) + setTextViewText(R.id.timetableWidgetItemTimeStart, lesson.start.toFormattedString("HH:mm")) + setTextViewText(R.id.timetableWidgetItemTimeFinish, lesson.end.toFormattedString("HH:mm")) + + updateDescription(this, lesson) + + if (lesson.canceled) { + updateStylesCanceled(this) + } else { + updateStylesNotCanceled(this, lesson) + } + setOnClickFillInIntent(R.id.timetableWidgetItemContainer, Intent()) } - - updateTheme() - clearLessonStyles(remoteViews) - - when { - lesson.canceled -> applyCancelledLessonStyles(remoteViews) - lesson.changes or lesson.info.isNotBlank() -> applyChangedLessonStyles( - remoteViews, lesson - ) - } - - return remoteViews } - private fun updateTheme() { - when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) { - Configuration.UI_MODE_NIGHT_YES -> { - textColor = android.R.color.white - timetableChangeColor = R.color.timetable_change_dark - timetableCanceledColor = R.color.timetable_canceled_dark - } - - else -> { - textColor = android.R.color.black - timetableChangeColor = R.color.timetable_change_light - timetableCanceledColor = R.color.timetable_canceled_light + private fun updateDescription(remoteViews: RemoteViews, lesson: Timetable) { + with(remoteViews) { + if (lesson.info.isNotBlank() && !lesson.changes) { + setTextViewText(R.id.timetableWidgetItemDescription, lesson.info) + setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE) + setViewVisibility(R.id.timetableWidgetItemRoom, GONE) + setViewVisibility(R.id.timetableWidgetItemTeacher, GONE) + } else { + setViewVisibility(R.id.timetableWidgetItemDescription, GONE) + setViewVisibility(R.id.timetableWidgetItemRoom, VISIBLE) + setViewVisibility(R.id.timetableWidgetItemTeacher, VISIBLE) } } } - private fun clearLessonStyles(remoteViews: RemoteViews) { - val defaultTextColor = context.getCompatColor(textColor ?: 0) + private fun updateStylesCanceled(remoteViews: RemoteViews) { + with(remoteViews) { + setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", + STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG) + setTextColor(R.id.timetableWidgetItemNumber, context.getCompatColor(primaryColor!!)) + setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor(primaryColor!!)) + setTextColor(R.id.timetableWidgetItemDescription, context.getCompatColor(primaryColor!!)) + } + } - remoteViews.apply { + private fun updateStylesNotCanceled(remoteViews: RemoteViews, lesson: Timetable) { + with(remoteViews) { setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", ANTI_ALIAS_FLAG) - setViewVisibility(R.id.timetableWidgetItemRoom, VISIBLE) - setViewVisibility(R.id.timetableWidgetItemTeacher, VISIBLE) - setViewVisibility(R.id.timetableWidgetItemIcon, GONE) - setViewVisibility(R.id.timetableWidgetItemDescription, GONE) - setTextColor(R.id.timetableWidgetItemNumber, defaultTextColor) - setTextColor(R.id.timetableWidgetItemSubject, defaultTextColor) - setTextColor(R.id.timetableWidgetItemRoom, defaultTextColor) - setTextColor(R.id.timetableWidgetItemTeacher, defaultTextColor) - setTextColor(R.id.timetableWidgetItemDescription, defaultTextColor) + setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor(textColor!!)) + setTextColor(R.id.timetableWidgetItemDescription, context.getCompatColor(timetableChangeColor!!)) + + updateNotCanceledLessonNumberColor(this, lesson) + updateNotCanceledSubjectColor(this, lesson) + + val teacherChange = lesson.teacherOld.isNotBlank() && lesson.teacher != lesson.teacherOld + updateNotCanceledRoom(this, lesson, teacherChange) + updateNotCanceledTeacher(this, lesson, teacherChange) } } - private fun applyCancelledLessonStyles(remoteViews: RemoteViews) { - val cancelledThemeColor = context.getCompatColor(timetableCanceledColor ?: 0) - val strikeThroughPaintFlags = STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG + private fun updateNotCanceledLessonNumberColor(remoteViews: RemoteViews, lesson: Timetable) { + remoteViews.setTextColor(R.id.timetableWidgetItemNumber, context.getCompatColor( + if (lesson.changes || (lesson.info.isNotBlank() && !lesson.canceled)) timetableChangeColor!! + else textColor!! + )) + } - remoteViews.apply { - setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", strikeThroughPaintFlags) - setTextColor(R.id.timetableWidgetItemNumber, cancelledThemeColor) - setTextColor(R.id.timetableWidgetItemSubject, cancelledThemeColor) - setTextColor(R.id.timetableWidgetItemDescription, cancelledThemeColor) - setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE) - setViewVisibility(R.id.timetableWidgetItemRoom, GONE) - setViewVisibility(R.id.timetableWidgetItemTeacher, GONE) + private fun updateNotCanceledSubjectColor(remoteViews: RemoteViews, lesson: Timetable) { + remoteViews.setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor( + if (lesson.subjectOld.isNotBlank() && lesson.subject != lesson.subjectOld) timetableChangeColor!! + else textColor!! + )) + } + + private fun updateNotCanceledRoom(remoteViews: RemoteViews, lesson: Timetable, teacherChange: Boolean) { + with(remoteViews) { + if (lesson.room.isNotBlank()) { + setTextViewText(R.id.timetableWidgetItemRoom, + if (teacherChange) lesson.room + else "${context.getString(R.string.timetable_room)} ${lesson.room}" + ) + + setTextColor(R.id.timetableWidgetItemRoom, context.getCompatColor( + if (lesson.roomOld.isNotBlank() && lesson.room != lesson.roomOld) timetableChangeColor!! + else textColor!! + )) + } else setTextViewText(R.id.timetableWidgetItemRoom, "") } } - private fun applyChangedLessonStyles(remoteViews: RemoteViews, lesson: Timetable) { - val changesTextColor = context.getCompatColor(timetableChangeColor ?: 0) - - remoteViews.apply { - setTextColor(R.id.timetableWidgetItemNumber, changesTextColor) - setTextColor(R.id.timetableWidgetItemDescription, changesTextColor) - setViewVisibility(R.id.timetableWidgetItemIcon, VISIBLE) - setImageViewResource(R.id.timetableWidgetItemIcon, R.drawable.ic_timetable_widget_swap) - } - - if (lesson.subject != lesson.subjectOld) { - remoteViews.setTextColor(R.id.timetableWidgetItemSubject, changesTextColor) - } - - if (lesson.room != lesson.roomOld) { - remoteViews.setTextColor(R.id.timetableWidgetItemRoom, changesTextColor) - } - - if (lesson.teacher != lesson.teacherOld) { - remoteViews.setTextColor(R.id.timetableWidgetItemTeacher, changesTextColor) - } - - if (lesson.info.isNotBlank() && !lesson.changes) { - remoteViews.setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE) - remoteViews.setViewVisibility(R.id.timetableWidgetItemRoom, GONE) - remoteViews.setViewVisibility(R.id.timetableWidgetItemTeacher, GONE) - } + private fun updateNotCanceledTeacher(remoteViews: RemoteViews, lesson: Timetable, teacherChange: Boolean) { + remoteViews.setTextViewText(R.id.timetableWidgetItemTeacher, + if (teacherChange) lesson.teacher + else "" + ) } - - private fun getSynchronizationInfoText(synchronizationInstant: Instant) = - synchronizationInstant.run { - val synchronizationTime = toFormattedString(TIME_FORMAT_STYLE) - val synchronizationDate = toFormattedString() - context.getString( - R.string.widget_timetable_last_synchronization, - synchronizationDate, - synchronizationTime, - ) - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt index 624ca30f..f9079b5f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt @@ -1,38 +1,50 @@ package io.github.wulkanowy.ui.modules.timetablewidget +import android.annotation.SuppressLint import android.app.PendingIntent +import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.appwidget.AppWidgetManager -import android.appwidget.AppWidgetManager.* -import android.content.BroadcastReceiver +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS import android.content.Context import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.content.res.Configuration +import android.graphics.Bitmap +import android.graphics.Canvas import android.widget.RemoteViews -import androidx.appcompat.content.res.AppCompatResources -import androidx.core.graphics.drawable.DrawableCompat -import androidx.core.graphics.drawable.toBitmap import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.services.HiltBroadcastReceiver import io.github.wulkanowy.services.widgets.TimetableWidgetService -import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.splash.SplashActivity -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.createNameInitialsDrawable +import io.github.wulkanowy.utils.getCompatColor +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.nextSchoolDay +import io.github.wulkanowy.utils.nickOrName +import io.github.wulkanowy.utils.previousSchoolDay +import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import timber.log.Timber import java.time.LocalDate -import java.time.LocalDateTime -import java.time.ZoneOffset +import java.time.LocalDate.now import javax.inject.Inject @AndroidEntryPoint -class TimetableWidgetProvider : BroadcastReceiver() { +class TimetableWidgetProvider : HiltBroadcastReceiver() { @Inject lateinit var appWidgetManager: AppWidgetManager @@ -48,8 +60,6 @@ class TimetableWidgetProvider : BroadcastReceiver() { companion object { - private const val TIMETABLE_PENDING_INTENT_ID = 201 - private const val EXTRA_TOGGLED_WIDGET_ID = "extraToggledWidget" private const val EXTRA_BUTTON_TYPE = "extraButtonType" @@ -60,20 +70,21 @@ class TimetableWidgetProvider : BroadcastReceiver() { private const val BUTTON_RESET = "buttonReset" - const val EXTRA_FROM_CONFIGURE = "extraFromConfigure" - const val EXTRA_FROM_PROVIDER = "extraFromProvider" fun getDateWidgetKey(appWidgetId: Int) = "timetable_widget_date_$appWidgetId" - fun getTodayLastLessonEndDateTimeWidgetKey(appWidgetId: Int) = - "timetable_widget_today_last_lesson_end_date_time_$appWidgetId" - fun getStudentWidgetKey(appWidgetId: Int) = "timetable_widget_student_$appWidgetId" + + fun getThemeWidgetKey(appWidgetId: Int) = "timetable_widget_theme_$appWidgetId" + + fun getCurrentThemeWidgetKey(appWidgetId: Int) = + "timetable_widget_current_theme_$appWidgetId" } @OptIn(DelicateCoroutinesApi::class) override fun onReceive(context: Context, intent: Intent) { + super.onReceive(context, intent) GlobalScope.launch { when (intent.action) { ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent) @@ -83,42 +94,31 @@ class TimetableWidgetProvider : BroadcastReceiver() { } private suspend fun onUpdate(context: Context, intent: Intent) { - if (intent.getStringExtra(EXTRA_BUTTON_TYPE) == null) { - val isFromConfigure = intent.getBooleanExtra(EXTRA_FROM_CONFIGURE, false) - val appWidgetIds = intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS) ?: return - - appWidgetIds.forEach { appWidgetId -> + if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) { + intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId -> val student = getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId) - val savedDataEpochDay = sharedPref.getLong(getDateWidgetKey(appWidgetId), 0) - - val dateToLoad = if (isFromConfigure && savedDataEpochDay != 0L) { - LocalDate.ofEpochDay(savedDataEpochDay) - } else { - getWidgetDefaultDateToLoad(appWidgetId) - } - - updateWidget(context, appWidgetId, dateToLoad, student) + updateWidget(context, appWidgetId, now().nextOrSameSchoolDay, student) } } else { val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE) val toggledWidgetId = intent.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0) val student = getStudent( - sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0), toggledWidgetId + sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0), + toggledWidgetId ) val savedDate = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0)) val date = when (buttonType) { - BUTTON_RESET -> getWidgetDefaultDateToLoad(toggledWidgetId) + BUTTON_RESET -> now().nextOrSameSchoolDay BUTTON_NEXT -> savedDate.nextSchoolDay BUTTON_PREV -> savedDate.previousSchoolDay - else -> getWidgetDefaultDateToLoad(toggledWidgetId) - } - if (!buttonType.isNullOrBlank()) { - analytics.logEvent( - "changed_timetable_widget_day", "button" to buttonType - ) + else -> now().nextOrSameSchoolDay } + if (!buttonType.isNullOrBlank()) analytics.logEvent( + "changed_timetable_widget_day", + "button" to buttonType + ) updateWidget(context, toggledWidgetId, date, student) } } @@ -130,64 +130,106 @@ class TimetableWidgetProvider : BroadcastReceiver() { with(sharedPref) { delete(getStudentWidgetKey(appWidgetId)) delete(getDateWidgetKey(appWidgetId)) + delete(getThemeWidgetKey(appWidgetId)) + delete(getCurrentThemeWidgetKey(appWidgetId)) } } } + @SuppressLint("DefaultLocale") private fun updateWidget( - context: Context, appWidgetId: Int, date: LocalDate, student: Student? + context: Context, + appWidgetId: Int, + date: LocalDate, + student: Student? ) { + val savedConfigureTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0) + val isSystemDarkMode = + context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES + var currentTheme = 0L + var layoutId = R.layout.widget_timetable + + if (savedConfigureTheme == 1L || (savedConfigureTheme == 2L && isSystemDarkMode)) { + currentTheme = 1L + layoutId = R.layout.widget_timetable_dark + } + val nextNavIntent = createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT) val prevNavIntent = createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV) val resetNavIntent = createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET) - val adapterIntent = Intent(context, TimetableWidgetService::class.java).apply { - putExtra(EXTRA_APPWIDGET_ID, appWidgetId) - action = appWidgetId.toString() //make Intent unique - } + val adapterIntent = Intent(context, TimetableWidgetService::class.java) + .apply { + putExtra(EXTRA_APPWIDGET_ID, appWidgetId) + //make Intent unique + action = appWidgetId.toString() + } + val accountIntent = PendingIntent.getActivity( + context, -Int.MAX_VALUE + appWidgetId, + Intent(context, TimetableWidgetConfigureActivity::class.java).apply { + addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK) + putExtra(EXTRA_APPWIDGET_ID, appWidgetId) + putExtra(EXTRA_FROM_PROVIDER, true) + }, FLAG_UPDATE_CURRENT + ) val appIntent = PendingIntent.getActivity( context, - TIMETABLE_PENDING_INTENT_ID, - SplashActivity.getStartIntent(context, Destination.Timetable()), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + MainView.Section.TIMETABLE.id, + MainActivity.getStartIntent(context, MainView.Section.TIMETABLE, true), + FLAG_UPDATE_CURRENT ) - val formattedDate = date.toFormattedString("EEE, dd.MM").capitalise() - val remoteView = RemoteViews(context.packageName, R.layout.widget_timetable).apply { + val remoteView = RemoteViews(context.packageName, layoutId).apply { setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty) - setTextViewText(R.id.timetableWidgetDate, formattedDate) + setTextViewText( + R.id.timetableWidgetDate, + date.toFormattedString("EEEE, dd.MM").capitalise() + ) + setTextViewText( + R.id.timetableWidgetName, + student?.nickOrName ?: context.getString(R.string.all_no_data) + ) + + student?.let { + setImageViewBitmap(R.id.timetableWidgetAccount, context.createAvatarBitmap(it)) + } + setRemoteAdapter(R.id.timetableWidgetList, adapterIntent) setOnClickPendingIntent(R.id.timetableWidgetNext, nextNavIntent) setOnClickPendingIntent(R.id.timetableWidgetPrev, prevNavIntent) setOnClickPendingIntent(R.id.timetableWidgetDate, resetNavIntent) + setOnClickPendingIntent(R.id.timetableWidgetName, resetNavIntent) + setOnClickPendingIntent(R.id.timetableWidgetAccount, accountIntent) setPendingIntentTemplate(R.id.timetableWidgetList, appIntent) } - student?.let { - setupAccountView(context, student, remoteView, appWidgetId) - } - with(sharedPref) { + putLong(getCurrentThemeWidgetKey(appWidgetId), currentTheme) putLong(getDateWidgetKey(appWidgetId), date.toEpochDay(), true) } with(appWidgetManager) { - partiallyUpdateAppWidget(appWidgetId, remoteView) + updateAppWidget(appWidgetId, remoteView) notifyAppWidgetViewDataChanged(appWidgetId, R.id.timetableWidgetList) + Timber.d("TimetableWidgetProvider updated") } - - Timber.d("TimetableWidgetProvider updated") } private fun createNavIntent( - context: Context, code: Int, appWidgetId: Int, buttonType: String - ) = PendingIntent.getBroadcast( - context, code, Intent(context, TimetableWidgetProvider::class.java).apply { - action = ACTION_APPWIDGET_UPDATE - putExtra(EXTRA_BUTTON_TYPE, buttonType) - putExtra(EXTRA_TOGGLED_WIDGET_ID, appWidgetId) - }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) + context: Context, + code: Int, + appWidgetId: Int, + buttonType: String + ): PendingIntent { + return PendingIntent.getBroadcast( + context, code, + Intent(context, TimetableWidgetProvider::class.java).apply { + action = ACTION_APPWIDGET_UPDATE + putExtra(EXTRA_BUTTON_TYPE, buttonType) + putExtra(EXTRA_TOGGLED_WIDGET_ID, appWidgetId) + }, FLAG_UPDATE_CURRENT + ) + } private suspend fun getStudent(studentId: Long, appWidgetId: Int) = try { val students = studentRepository.getSavedStudents(false) @@ -208,60 +250,28 @@ class TimetableWidgetProvider : BroadcastReceiver() { null } - private fun getWidgetDefaultDateToLoad(appWidgetId: Int): LocalDate { - val lastLessonEndTimestamp = - sharedPref.getLong(getTodayLastLessonEndDateTimeWidgetKey(appWidgetId), 0) - val lastLessonEndDateTime = - LocalDateTime.ofEpochSecond(lastLessonEndTimestamp, 0, ZoneOffset.UTC) - - val todayDate = LocalDate.now() - val isLastLessonEndDateNow = lastLessonEndDateTime.toLocalDate() == todayDate - val isLastLessonEndDateAfterNowTime = LocalDateTime.now() > lastLessonEndDateTime - - return if (isLastLessonEndDateNow && isLastLessonEndDateAfterNowTime) { - todayDate.nextSchoolDay + private fun Context.createAvatarBitmap(student: Student): Bitmap { + val avatarColor = if (student.avatarColor == -2937041L) { + getCompatColor(R.color.colorPrimaryLight).toLong() } else { - todayDate.nextOrSameSchoolDay + student.avatarColor } - } + val avatarDrawable = createNameInitialsDrawable(student.nickOrName, avatarColor, 0.5f) - private fun setupAccountView( - context: Context, - student: Student, - remoteViews: RemoteViews, - appWidgetId: Int - ) { - val accountInitials = student.nickOrName - .split(" ") - .mapNotNull { it.firstOrNull() }.take(2) - .joinToString(separator = "").uppercase() - - val accountPickerIntent = PendingIntent.getActivity( - context, - -Int.MAX_VALUE + appWidgetId, - Intent(context, TimetableWidgetConfigureActivity::class.java).apply { - addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK) - putExtra(EXTRA_APPWIDGET_ID, appWidgetId) - putExtra(EXTRA_FROM_PROVIDER, true) - }, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) - - // Create background bitmap - val avatarDrawableResource = R.drawable.background_timetable_widget_avatar - AppCompatResources.getDrawable(context, avatarDrawableResource)?.let { drawable -> - val screenDensity = context.resources.displayMetrics.density - val avatarSize = (48 * screenDensity).toInt() - val backgroundBitmap = DrawableCompat.wrap(drawable).run { - DrawableCompat.setTint(this, student.avatarColor.toInt()) - toBitmap(avatarSize, avatarSize) + val avatarBitmap = + if (avatarDrawable.intrinsicWidth <= 0 || avatarDrawable.intrinsicHeight <= 0) { + Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + } else { + Bitmap.createBitmap( + avatarDrawable.intrinsicWidth, + avatarDrawable.intrinsicHeight, + Bitmap.Config.ARGB_8888 + ) } - remoteViews.setImageViewBitmap(R.id.timetableWidgetAccountBackground, backgroundBitmap) - } - remoteViews.apply { - setTextViewText(R.id.timetableWidgetAccountInitials, accountInitials) - setOnClickPendingIntent(R.id.timetableWidgetAccount, accountPickerIntent) - } + val canvas = Canvas(avatarBitmap) + avatarDrawable.setBounds(0, 0, canvas.width, canvas.height) + avatarDrawable.draw(canvas) + return avatarBitmap } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/FittedScrollableTabLayout.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/FittedScrollableTabLayout.kt index 6b7fb4aa..0f121dc5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/FittedScrollableTabLayout.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/FittedScrollableTabLayout.kt @@ -3,15 +3,17 @@ package io.github.wulkanowy.ui.widgets import android.content.Context import android.util.AttributeSet import android.view.ViewGroup -import com.google.android.material.tabs.TabLayout /** * @see Tabs don't fit to screen with tabmode=scrollable, Even with a Custom Tab Layout */ -class FittedScrollableTabLayout @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null -) : TabLayout(context, attrs) { +class FittedScrollableTabLayout : MaterialTabLayout { + + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { setMeasuredDimension(widthMeasureSpec, heightMeasureSpec) diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialLinearLayout.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialLinearLayout.kt index 4e1ca1a9..a04922e5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialLinearLayout.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialLinearLayout.kt @@ -1,19 +1,24 @@ package io.github.wulkanowy.ui.widgets import android.content.Context +import android.os.Build.VERSION.SDK_INT +import android.os.Build.VERSION_CODES.LOLLIPOP import android.util.AttributeSet import android.widget.LinearLayout import androidx.core.view.ViewCompat +import com.google.android.material.elevation.ElevationOverlayProvider import com.google.android.material.shape.MaterialShapeDrawable -class MaterialLinearLayout @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null -) : LinearLayout(context, attrs) { +class MaterialLinearLayout : LinearLayout { + + constructor(context: Context) : super(context) + + constructor(context: Context, attr: AttributeSet) : super(context, attr) + + constructor(context: Context, attr: AttributeSet, defStyleAttr: Int) : super(context, attr, defStyleAttr) init { - val drawable = - MaterialShapeDrawable.createWithElevationOverlay(context, ViewCompat.getElevation(this)) + val drawable = MaterialShapeDrawable.createWithElevationOverlay(context, ViewCompat.getElevation(this)) ViewCompat.setBackground(this, drawable) } @@ -23,4 +28,12 @@ class MaterialLinearLayout @JvmOverloads constructor( (background as MaterialShapeDrawable).elevation = elevation } } + + fun setElevationCompat(elevation: Float) { + if (SDK_INT >= LOLLIPOP) { + setElevation(elevation) + } else { + setBackgroundColor(ElevationOverlayProvider(context).compositeOverlayWithThemeSurfaceColorIfNeeded(elevation)) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialTabLayout.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialTabLayout.kt new file mode 100644 index 00000000..e19d0111 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/MaterialTabLayout.kt @@ -0,0 +1,25 @@ +package io.github.wulkanowy.ui.widgets + +import android.content.Context +import android.os.Build.VERSION.SDK_INT +import android.os.Build.VERSION_CODES.LOLLIPOP +import android.util.AttributeSet +import com.google.android.material.elevation.ElevationOverlayProvider +import com.google.android.material.tabs.TabLayout + +open class MaterialTabLayout : TabLayout { + + constructor(context: Context) : super(context) + + constructor(context: Context, attr: AttributeSet) : super(context, attr) + + constructor(context: Context, attr: AttributeSet, defStyleAttr: Int) : super(context, attr, defStyleAttr) + + fun setElevationCompat(elevation: Float) { + if (SDK_INT >= LOLLIPOP) { + setElevation(elevation) + } else { + setBackgroundColor(ElevationOverlayProvider(context).compositeOverlayWithThemeSurfaceColorIfNeeded(elevation)) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/widgets/SwipeDisabledViewPager.kt b/app/src/main/java/io/github/wulkanowy/ui/widgets/SwipeDisabledViewPager.kt new file mode 100644 index 00000000..eb5cae4f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/widgets/SwipeDisabledViewPager.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.ui.widgets + +import android.annotation.SuppressLint +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import androidx.viewpager.widget.ViewPager + +class SwipeDisabledViewPager : ViewPager { + + constructor(context: Context) : super(context) + + constructor(context: Context, attr: AttributeSet) : super(context, attr) + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(ev: MotionEvent) = false + + override fun onInterceptTouchEvent(ev: MotionEvent) = false +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt b/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt index 962e5b20..a3961aed 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt @@ -1,31 +1,35 @@ package io.github.wulkanowy.utils import android.content.res.Resources -import android.os.Build -import io.github.wulkanowy.BuildConfig +import android.os.Build.MANUFACTURER +import android.os.Build.MODEL +import android.os.Build.VERSION.SDK_INT +import io.github.wulkanowy.BuildConfig.BUILD_TIMESTAMP +import io.github.wulkanowy.BuildConfig.DEBUG +import io.github.wulkanowy.BuildConfig.FLAVOR +import io.github.wulkanowy.BuildConfig.VERSION_CODE +import io.github.wulkanowy.BuildConfig.VERSION_NAME import javax.inject.Inject import javax.inject.Singleton @Singleton open class AppInfo @Inject constructor() { - open val isDebug get() = BuildConfig.DEBUG + open val isDebug get() = DEBUG - open val versionCode get() = BuildConfig.VERSION_CODE + open val versionCode get() = VERSION_CODE - open val buildTimestamp get() = BuildConfig.BUILD_TIMESTAMP + open val buildTimestamp get() = BUILD_TIMESTAMP - open val buildFlavor get() = BuildConfig.FLAVOR + open val buildFlavor get() = FLAVOR - open val versionName get() = BuildConfig.VERSION_NAME + open val versionName get() = VERSION_NAME - open val systemVersion get() = Build.VERSION.SDK_INT + open val systemVersion get() = SDK_INT - open val systemManufacturer: String get() = Build.MANUFACTURER + open val systemManufacturer: String get() = MANUFACTURER - open val systemModel: String get() = Build.MODEL - - open val messagesBaseUrl = BuildConfig.MESSAGES_BASE_URL + open val systemModel: String get() = MODEL @Suppress("DEPRECATION") open val systemLanguage: String diff --git a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt index 397c9595..479cc518 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt @@ -17,7 +17,7 @@ private inline val AttendanceSummary.allAbsences: Double get() = absence.toDouble() + absenceExcused inline val Attendance.isExcusableOrNotExcused: Boolean - get() = (excusable || ((absence || lateness) && !excused)) && excuseStatus == null + get() = excusable || ((absence || lateness) && !excused) fun AttendanceSummary.calculatePercentage() = calculatePercentage(allPresences, allAbsences) @@ -29,7 +29,7 @@ private fun calculatePercentage(presence: Double, absence: Double): Double { return if ((presence + absence) == 0.0) 0.0 else (presence / (presence + absence)) * 100 } -inline val Attendance.descriptionRes +inline val Attendance.description get() = when (AttendanceCategory.getCategoryByName(name)) { AttendanceCategory.PRESENCE -> R.string.attendance_present AttendanceCategory.ABSENCE_UNEXCUSED -> R.string.attendance_absence_unexcused diff --git a/app/src/main/java/io/github/wulkanowy/utils/BaseRemoteConfigHelper.kt b/app/src/main/java/io/github/wulkanowy/utils/BaseRemoteConfigHelper.kt deleted file mode 100644 index 002612a8..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/BaseRemoteConfigHelper.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.wulkanowy.utils - -abstract class BaseRemoteConfigHelper { - - open fun initialize() = Unit - - open val userAgentTemplate: String - get() = RemoteConfigDefaults.USER_AGENT_TEMPLATE.value as String -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt deleted file mode 100644 index d3c9f800..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.utils - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.os.Parcelable -import java.io.Serializable - -inline fun Bundle.serializable(key: String): T = when { - Build.VERSION.SDK_INT >= 33 -> getSerializable(key, T::class.java)!! - else -> @Suppress("DEPRECATION") getSerializable(key) as T -} - -inline fun Bundle.nullableSerializable(key: String): T? = when { - Build.VERSION.SDK_INT >= 33 -> getSerializable(key, T::class.java) - else -> @Suppress("DEPRECATION") getSerializable(key) as T? -} - -@Suppress("UNCHECKED_CAST") -inline fun Bundle.parcelableArray(key: String): Array? = when { - Build.VERSION.SDK_INT >= 33 -> getParcelableArray(key, T::class.java) - else -> @Suppress("DEPRECATION") getParcelableArray(key) as Array? -} - -inline fun Intent.serializable(key: String): T = when { - Build.VERSION.SDK_INT >= 33 -> getSerializableExtra(key, T::class.java)!! - else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T -} - -inline fun Intent.nullableSerializable(key: String): T? = when { - Build.VERSION.SDK_INT >= 33 -> getSerializableExtra(key, T::class.java) - else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T? -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt index 77f3eb64..cb31389e 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -2,18 +2,28 @@ package io.github.wulkanowy.utils import android.annotation.SuppressLint import android.content.Context -import android.content.res.ColorStateList -import android.graphics.* +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.Paint +import android.graphics.PorterDuff +import android.graphics.PorterDuffColorFilter +import android.graphics.Rect +import android.graphics.Typeface +import android.net.Uri import android.text.TextPaint import android.util.DisplayMetrics.DENSITY_DEFAULT -import androidx.annotation.* +import androidx.annotation.AttrRes +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils import androidx.core.graphics.applyCanvas import androidx.core.graphics.drawable.RoundedBitmapDrawable import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory import androidx.core.graphics.drawable.toBitmap - +import io.github.wulkanowy.BuildConfig.APPLICATION_ID @ColorInt fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int { @@ -46,8 +56,63 @@ fun Context.getCompatDrawable(@DrawableRes drawableRes: Int, @ColorRes colorRes: fun Context.getCompatBitmap(@DrawableRes drawableRes: Int, @ColorRes colorRes: Int) = getCompatDrawable(drawableRes, colorRes)?.toBitmap() -fun Context.getPlural(@PluralsRes pluralRes: Int, quantity: Int, vararg arguments: Any) = - resources.getQuantityString(pluralRes, quantity, *arguments) +fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) -> Unit = {}) { + Intent.parseUri(uri, 0).let { + if (it.resolveActivity(packageManager) != null) startActivity(it) + else onActivityNotFound(uri) + } +} + +fun Context.openAppInMarket(onActivityNotFound: (uri: String) -> Unit) { + openInternetBrowser("market://details?id=${APPLICATION_ID}") { + openInternetBrowser("https://github.com/wulkanowy/wulkanowy/releases", onActivityNotFound) + } +} + +fun Context.openEmailClient( + chooserTitle: String, + email: String, + subject: String, + body: String, + onActivityNotFound: () -> Unit = {} +) { + val intent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")).apply { + putExtra(Intent.EXTRA_EMAIL, arrayOf(email)) + putExtra(Intent.EXTRA_SUBJECT, subject) + putExtra(Intent.EXTRA_TEXT, body) + } + + if (intent.resolveActivity(packageManager) != null) { + startActivity(Intent.createChooser(intent, chooserTitle)) + } else onActivityNotFound() +} + +fun Context.openNavigation(location: String) { + val intentUri = Uri.parse("geo:0,0?q=${Uri.encode(location)}") + val intent = Intent(Intent.ACTION_VIEW, intentUri) + if (intent.resolveActivity(packageManager) != null) { + startActivity(intent) + } +} + +fun Context.openDialer(phone: String) { + val intentUri = Uri.parse("tel:$phone") + val intent = Intent(Intent.ACTION_DIAL, intentUri) + startActivity(intent) +} + +fun Context.shareText(text: String, subject: String?) { + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_TEXT, text) + if (subject != null) { + putExtra(Intent.EXTRA_SUBJECT, subject) + } + type = "text/plain" + } + val shareIntent = Intent.createChooser(sendIntent, null) + startActivity(shareIntent) +} fun Context.dpToPx(dp: Float) = dp * resources.displayMetrics.densityDpi / DENSITY_DEFAULT @@ -86,7 +151,3 @@ fun Context.createNameInitialsDrawable( return RoundedBitmapDrawableFactory.create(this.resources, bitmap) .apply { isCircular = true } } - -fun Context.getAttrColorStateList(@AttrRes color: Int): ColorStateList { - return ColorStateList.valueOf(getThemeAttrColor(color)) -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/DispatchersProvider.kt b/app/src/main/java/io/github/wulkanowy/utils/DispatchersProvider.kt index 8aaa57f4..ecc8e05e 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/DispatchersProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/DispatchersProvider.kt @@ -1,8 +1,10 @@ package io.github.wulkanowy.utils +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers open class DispatchersProvider { - open val io get() = Dispatchers.IO + open val backgroundThread: CoroutineDispatcher + get() = Dispatchers.IO } diff --git a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt deleted file mode 100644 index a4c2537a..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt +++ /dev/null @@ -1,76 +0,0 @@ -package io.github.wulkanowy.utils - -import android.content.res.Resources -import io.github.wulkanowy.R -import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException -import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException -import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException -import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException -import io.github.wulkanowy.sdk.scrapper.exception.VulcanException -import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException -import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException -import okhttp3.internal.http2.StreamResetException -import java.io.InterruptedIOException -import java.net.ConnectException -import java.net.SocketException -import java.net.SocketTimeoutException -import java.net.UnknownHostException -import java.security.cert.CertPathValidatorException -import java.security.cert.CertificateExpiredException -import java.security.cert.CertificateNotYetValidException -import javax.net.ssl.SSLHandshakeException - -fun Resources.getErrorString(error: Throwable): String = when (error) { - is UnknownHostException -> R.string.error_no_internet - is ConnectException, - is SocketException, - is SocketTimeoutException, - is InterruptedIOException, - is StreamResetException -> R.string.error_timeout - is NotLoggedInException -> R.string.error_login_failed - is PasswordChangeRequiredException -> R.string.error_password_change_required - is ServiceUnavailableException -> R.string.error_service_unavailable - is FeatureDisabledException -> R.string.error_feature_disabled - is FeatureNotAvailableException -> R.string.error_feature_not_available - is VulcanException -> R.string.error_unknown_uonet - is ScrapperException -> R.string.error_unknown_app - is SSLHandshakeException -> when { - error.isCausedByCertificateNotValidNow() -> R.string.error_invalid_device_datetime - else -> R.string.error_timeout - } - else -> R.string.error_unknown -}.let { getString(it) } - -fun Throwable.isShouldBeReported(): Boolean = when (this) { - is UnknownHostException, - is ConnectException, - is SocketException, - is SocketTimeoutException, - is InterruptedIOException, - is StreamResetException, - is ServiceUnavailableException, - is FeatureDisabledException, - is FeatureNotAvailableException -> false - is SSLHandshakeException -> when { - isCausedByCertificateNotValidNow() -> false - else -> true - } - else -> true -} - -private fun Throwable?.isCausedByCertificateNotValidNow(): Boolean { - var exception = this - do { - if (exception.isCertificateNotValidNow()) return true - - exception = exception?.cause - } while (exception != null) - return false -} - -private fun Throwable?.isCertificateNotValidNow(): Boolean { - val isNotYetValid = this is CertificateNotYetValidException - val isExpired = this is CertificateExpiredException - val isInvalidPath = this is CertPathValidatorException - return isNotYetValid || isExpired || isInvalidPath -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt new file mode 100644 index 00000000..5dd28967 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt @@ -0,0 +1,96 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.Status +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.takeWhile +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock + +inline fun networkBoundResource( + mutex: Mutex = Mutex(), + showSavedOnLoading: Boolean = true, + crossinline query: () -> Flow, + crossinline fetch: suspend (ResultType) -> RequestType, + crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit, + crossinline onFetchFailed: (Throwable) -> Unit = { }, + crossinline shouldFetch: (ResultType) -> Boolean = { true }, + crossinline filterResult: (ResultType) -> ResultType = { it } +) = flow { + emit(Resource.loading()) + + val data = query().first() + emitAll(if (shouldFetch(data)) { + if (showSavedOnLoading) emit(Resource.loading(filterResult(data))) + + try { + val newData = fetch(data) + mutex.withLock { saveFetchResult(query().first(), newData) } + query().map { Resource.success(filterResult(it)) } + } catch (throwable: Throwable) { + onFetchFailed(throwable) + query().map { Resource.error(throwable, filterResult(it)) } + } + } else { + query().map { Resource.success(filterResult(it)) } + }) +} + +@JvmName("networkBoundResourceWithMap") +inline fun networkBoundResource( + mutex: Mutex = Mutex(), + showSavedOnLoading: Boolean = true, + crossinline query: () -> Flow, + crossinline fetch: suspend (ResultType) -> RequestType, + crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit, + crossinline onFetchFailed: (Throwable) -> Unit = { }, + crossinline shouldFetch: (ResultType) -> Boolean = { true }, + crossinline mapResult: (ResultType) -> T +) = flow { + emit(Resource.loading()) + + val data = query().first() + emitAll(if (shouldFetch(data)) { + if (showSavedOnLoading) emit(Resource.loading(mapResult(data))) + + try { + val newData = fetch(data) + mutex.withLock { saveFetchResult(query().first(), newData) } + query().map { Resource.success(mapResult(it)) } + } catch (throwable: Throwable) { + onFetchFailed(throwable) + query().map { Resource.error(throwable, mapResult(it)) } + } + } else { + query().map { Resource.success(mapResult(it)) } + }) +} + +fun flowWithResource(block: suspend () -> T) = flow { + emit(Resource.loading()) + emit(Resource.success(block())) +}.catch { emit(Resource.error(it)) } + +@OptIn(FlowPreview::class) +fun flowWithResourceIn(block: suspend () -> Flow>) = flow { + emit(Resource.loading()) + emitAll(block().filter { it.status != Status.LOADING || (it.status == Status.LOADING && it.data != null) }) +}.catch { emit(Resource.error(it)) } + +fun Flow>.afterLoading(callback: () -> Unit) = onEach { + if (it.status != Status.LOADING) callback() +} + +suspend fun Flow>.toFirstResult() = filter { it.status != Status.LOADING }.first() + +suspend fun Flow>.waitForResult() = + takeWhile { it.status == Status.LOADING }.collect() diff --git a/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt index 01c876dd..9dc1e18a 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt @@ -2,19 +2,16 @@ package io.github.wulkanowy.utils import androidx.fragment.app.Fragment import com.ncapdevi.fragnav.FragNavController -import io.github.wulkanowy.ui.base.BaseView +import io.github.wulkanowy.ui.modules.main.MainView -inline fun FragNavController.setOnViewChangeListener(crossinline listener: (view: BaseView) -> Unit) { +inline fun FragNavController.setOnViewChangeListener(crossinline listener: (section: MainView.Section?, name: String?) -> Unit) { transactionListener = object : FragNavController.TransactionListener { - override fun onFragmentTransaction( - fragment: Fragment?, - transactionType: FragNavController.TransactionType - ) { - fragment?.let { listener(it as BaseView) } + override fun onFragmentTransaction(fragment: Fragment?, transactionType: FragNavController.TransactionType) { + listener(fragment?.toSection(), fragment?.let { it::class.java.simpleName }) } override fun onTabTransaction(fragment: Fragment?, index: Int) { - fragment?.let { listener(it as BaseView) } + listener(fragment?.toSection(), fragment?.let { it::class.java.simpleName }) } } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/FragmentExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/FragmentExtension.kt new file mode 100644 index 00000000..210a6209 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/FragmentExtension.kt @@ -0,0 +1,44 @@ +package io.github.wulkanowy.utils + +import androidx.fragment.app.Fragment +import io.github.wulkanowy.ui.modules.account.AccountFragment +import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment +import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment +import io.github.wulkanowy.ui.modules.conference.ConferenceFragment +import io.github.wulkanowy.ui.modules.dashboard.DashboardFragment +import io.github.wulkanowy.ui.modules.exam.ExamFragment +import io.github.wulkanowy.ui.modules.grade.GradeFragment +import io.github.wulkanowy.ui.modules.homework.HomeworkFragment +import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.message.MessageFragment +import io.github.wulkanowy.ui.modules.more.MoreFragment +import io.github.wulkanowy.ui.modules.note.NoteFragment +import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment +import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment +import io.github.wulkanowy.ui.modules.settings.SettingsFragment +import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment +import io.github.wulkanowy.ui.modules.timetable.TimetableFragment + +fun Fragment.toSection(): MainView.Section? { + return when (this) { + is GradeFragment -> MainView.Section.GRADE + is AttendanceFragment -> MainView.Section.ATTENDANCE + is ExamFragment -> MainView.Section.EXAM + is TimetableFragment -> MainView.Section.TIMETABLE + is MoreFragment -> MainView.Section.MORE + is MessageFragment -> MainView.Section.MESSAGE + is HomeworkFragment -> MainView.Section.HOMEWORK + is NoteFragment -> MainView.Section.NOTE + is LuckyNumberFragment -> MainView.Section.LUCKY_NUMBER + is SettingsFragment -> MainView.Section.SETTINGS + is SchoolAndTeachersFragment -> MainView.Section.SCHOOL + is AccountFragment -> MainView.Section.ACCOUNT + is AccountDetailsFragment -> MainView.Section.ACCOUNT + is StudentInfoFragment -> MainView.Section.STUDENT_INFO + is ConferenceFragment -> MainView.Section.CONFERENCE + is SchoolAnnouncementFragment -> MainView.Section.SCHOOL_ANNOUNCEMENT + is DashboardFragment -> MainView.Section.DASHBOARD + else -> null + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt index 61924d4e..820e7f43 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt @@ -3,9 +3,7 @@ package io.github.wulkanowy.utils import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.enums.GradeColorTheme -import io.github.wulkanowy.sdk.scrapper.grades.getGradeValueWithModifier -import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid +import io.github.wulkanowy.sdk.scrapper.grades.* fun List.calcAverage(isOptionalArithmeticAverage: Boolean): Double { val isArithmeticAverage = isOptionalArithmeticAverage && !any { it.weightValue != .0 } @@ -20,20 +18,48 @@ fun List.calcAverage(isOptionalArithmeticAverage: Boolean): Double { return if (denominator != 0.0) counter / denominator else 0.0 } -fun List.calcFinalAverage(plusModifier: Double, minusModifier: Double) = asSequence() - .mapNotNull { summary -> - val (gradeValue, gradeModifier) = getGradeValueWithModifier(summary.finalGrade) - if (gradeValue == null || gradeModifier == null) return@mapNotNull null - - when { - gradeModifier > 0 -> gradeValue + plusModifier - gradeModifier < 0 -> gradeValue - minusModifier - else -> gradeValue + 0.0 - } +@JvmName("calcSummaryAverage") +fun List.calcAverage(plusModifier: Double, minusModifier: Double) = asSequence() + .mapNotNull { + if (it.finalGrade.matches("[0-6][+-]?".toRegex())) { + when { + it.finalGrade.endsWith('+') -> { + it.finalGrade.removeSuffix("+").toDouble() + plusModifier + } + it.finalGrade.endsWith('-') -> { + it.finalGrade.removeSuffix("-").toDouble() - minusModifier + } + else -> { + it.finalGrade.toDouble() + } + } + } else null } .average() .let { if (it.isNaN()) 0.0 else it } +fun Grade.getBackgroundColor(theme: String) = when (theme) { + "grade_color" -> getGradeColor() + "material" -> when (value.toInt()) { + 6 -> R.color.grade_material_six + 5 -> R.color.grade_material_five + 4 -> R.color.grade_material_four + 3 -> R.color.grade_material_three + 2 -> R.color.grade_material_two + 1 -> R.color.grade_material_one + else -> R.color.grade_material_default + } + else -> when (value.toInt()) { + 6 -> R.color.grade_vulcan_six + 5 -> R.color.grade_vulcan_five + 4 -> R.color.grade_vulcan_four + 3 -> R.color.grade_vulcan_three + 2 -> R.color.grade_vulcan_two + 1 -> R.color.grade_vulcan_one + else -> R.color.grade_vulcan_default + } +} + fun Grade.getGradeColor() = when (color) { "000000" -> R.color.grade_black "F04C4C" -> R.color.grade_red @@ -58,25 +84,3 @@ fun Grade.changeModifier(plusModifier: Double, minusModifier: Double) = when { modifier < 0 -> copy(modifier = -minusModifier) else -> this } - -fun Grade.getBackgroundColor(theme: GradeColorTheme) = when (theme) { - GradeColorTheme.GRADE_COLOR -> getGradeColor() - GradeColorTheme.MATERIAL -> when (value.toInt()) { - 6 -> R.color.grade_material_six - 5 -> R.color.grade_material_five - 4 -> R.color.grade_material_four - 3 -> R.color.grade_material_three - 2 -> R.color.grade_material_two - 1 -> R.color.grade_material_one - else -> R.color.grade_material_default - } - GradeColorTheme.VULCAN -> when (value.toInt()) { - 6 -> R.color.grade_vulcan_six - 5 -> R.color.grade_vulcan_five - 4 -> R.color.grade_vulcan_four - 3 -> R.color.grade_vulcan_three - 2 -> R.color.grade_vulcan_two - 1 -> R.color.grade_vulcan_one - else -> R.color.grade_vulcan_default - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt deleted file mode 100644 index 62b85af4..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt +++ /dev/null @@ -1,121 +0,0 @@ -package io.github.wulkanowy.utils - -import android.app.Activity -import android.content.ActivityNotFoundException -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.Build -import android.provider.CalendarContract -import android.provider.Settings -import io.github.wulkanowy.BuildConfig -import timber.log.Timber -import java.time.LocalDateTime -import java.time.ZoneId - -fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) -> Unit = {}) { - Intent.parseUri(uri, 0).let { - try { - startActivity(it) - } catch (e: ActivityNotFoundException) { - onActivityNotFound(uri) - } - } -} - -fun Context.openAppInMarket(onActivityNotFound: (uri: String) -> Unit) { - openInternetBrowser("market://details?id=${BuildConfig.APPLICATION_ID}") { - openInternetBrowser("https://github.com/wulkanowy/wulkanowy/releases", onActivityNotFound) - } -} - -fun Context.openEmailClient( - chooserTitle: String, - email: String, - subject: String, - body: String, - onActivityNotFound: () -> Unit = {} -) { - val intent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")).apply { - putExtra(Intent.EXTRA_EMAIL, arrayOf(email)) - putExtra(Intent.EXTRA_SUBJECT, subject) - putExtra(Intent.EXTRA_TEXT, body) - } - - if (intent.resolveActivity(packageManager) != null) { - startActivity(Intent.createChooser(intent, chooserTitle)) - } else onActivityNotFound() -} - -fun Context.openCalendarEventAdd( - title: String, - description: String, - start: LocalDateTime, - end: LocalDateTime? = null, - isAllDay: Boolean = false, - onActivityNotFound: (uri: String?) -> Unit = {}, -) { - val beginTime = start.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - val endTime = end?.atZone(ZoneId.systemDefault())?.toInstant()?.toEpochMilli() - - val intent = Intent(Intent.ACTION_INSERT) - .setData(CalendarContract.Events.CONTENT_URI) - .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime) - .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime) - .putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay) - .putExtra(CalendarContract.Events.TITLE, title) - .putExtra(CalendarContract.Events.DESCRIPTION, description) - .putExtra(CalendarContract.Events.AVAILABILITY, CalendarContract.Events.AVAILABILITY_BUSY) - - try { - startActivity(intent) - } catch (e: ActivityNotFoundException) { - onActivityNotFound(intent.dataString) - } -} - -fun Context.openNavigation(location: String) { - val intentUri = Uri.parse("geo:0,0?q=${Uri.encode(location)}") - val intent = Intent(Intent.ACTION_VIEW, intentUri) - if (intent.resolveActivity(packageManager) != null) { - startActivity(intent) - } -} - -fun Context.openDialer(phone: String) { - val intentUri = Uri.parse("tel:$phone") - val intent = Intent(Intent.ACTION_DIAL, intentUri) - if (intent.resolveActivity(packageManager) != null) { - startActivity(intent) - } -} - -fun Activity.openNotificationSettings() { - val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { - putExtra("android.provider.extra.APP_PACKAGE", packageName) - } - } else { - Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { - data = Uri.fromParts("package", packageName, null) - } - } - try { - startActivity(intent) - } catch (e: Exception) { - Timber.e(e) - } -} - -fun Context.shareText(text: String, subject: String?) { - val sendIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_TEXT, text) - if (subject != null) { - putExtra(Intent.EXTRA_SUBJECT, subject) - } - type = "text/plain" - } - val shareIntent = Intent.createChooser(sendIntent, null) - startActivity(shareIntent) -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt b/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt index 76ce66dc..d2a8908c 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt @@ -3,14 +3,14 @@ package io.github.wulkanowy.utils import android.os.Handler import android.os.Looper import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.OnLifecycleEvent import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty -class LifecycleAwareVariable : ReadWriteProperty, DefaultLifecycleObserver { +class LifecycleAwareVariable : ReadWriteProperty, LifecycleObserver { private var _value: T? = null @@ -23,28 +23,30 @@ class LifecycleAwareVariable : ReadWriteProperty, DefaultL override fun getValue(thisRef: Fragment, property: KProperty<*>) = _value ?: throw IllegalStateException("Trying to call an lifecycle-aware value outside of the view lifecycle, or the value has not been initialized") - override fun onDestroy(owner: LifecycleOwner) { - Handler(Looper.getMainLooper()).post { - _value = null - } + @Suppress("unused") + @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) + fun onDestroyView() { + _value = null } } -class LifecycleAwareVariableComponent : ReadWriteProperty, - DefaultLifecycleObserver { +class LifecycleAwareVariableActivity : ReadWriteProperty, + LifecycleObserver { private var _value: T? = null - override fun setValue(thisRef: LifecycleOwner, property: KProperty<*>, value: T) { + override fun setValue(thisRef: AppCompatActivity, property: KProperty<*>, value: T) { thisRef.lifecycle.removeObserver(this) _value = value thisRef.lifecycle.addObserver(this) } - override fun getValue(thisRef: LifecycleOwner, property: KProperty<*>) = _value + override fun getValue(thisRef: AppCompatActivity, property: KProperty<*>) = _value ?: throw IllegalStateException("Trying to call an lifecycle-aware value outside of the view lifecycle, or the value has not been initialized") - override fun onDestroy(owner: LifecycleOwner) { + @Suppress("unused") + @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) + fun onDestroyView() { Handler(Looper.getMainLooper()).post { _value = null } @@ -54,8 +56,4 @@ class LifecycleAwareVariableComponent : ReadWriteProperty Fragment.lifecycleAwareVariable() = LifecycleAwareVariable() -@Suppress("unused") -fun DialogFragment.lifecycleAwareVariable() = LifecycleAwareVariableComponent() - -@Suppress("unused") -fun AppCompatActivity.lifecycleAwareVariable() = LifecycleAwareVariableComponent() +fun lifecycleAwareVariable() = LifecycleAwareVariableActivity() diff --git a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt index 1e9f49a6..ee18453f 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt @@ -123,6 +123,14 @@ class FragmentLifecycleLogger @Inject constructor() : Timber.d("${f::class.java.simpleName} VIEW DESTROYED") } + override fun onFragmentActivityCreated( + fm: FragmentManager, + f: Fragment, + savedInstanceState: Bundle? + ) { + Timber.d("${f::class.java.simpleName} ACTIVITY CREATED ${savedInstanceState.checkSavedState()}") + } + override fun onFragmentPaused(fm: FragmentManager, f: Fragment) { Timber.d("${f::class.java.simpleName} PAUSED") } @@ -133,5 +141,5 @@ class FragmentLifecycleLogger @Inject constructor() : } private fun Bundle?.checkSavedState() = - if (this == null) "(STATE IS NULL)" else "(RESTORE STATE)" + if (this == null) "(STATE IS NULL)" else "(STATE IS NOT NULL)" diff --git a/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt deleted file mode 100644 index 09ccda89..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.utils - -import androidx.fragment.app.Fragment -import com.google.android.material.datepicker.CalendarConstraints -import com.google.android.material.datepicker.MaterialDatePicker -import kotlinx.parcelize.Parcelize -import java.time.LocalDate -import java.time.temporal.ChronoUnit - -fun Fragment.openMaterialDatePicker( - selected: LocalDate, - rangeStart: LocalDate, - rangeEnd: LocalDate, - onDateSelected: (LocalDate) -> Unit, -) { - val constraintsBuilder = CalendarConstraints.Builder().apply { - setValidator(CalendarDayRangeValidator(rangeStart, rangeEnd)) - setStart(rangeStart.toTimestamp()) - setEnd(rangeEnd.toTimestamp()) - } - - val datePicker = MaterialDatePicker.Builder.datePicker() - .setCalendarConstraints(constraintsBuilder.build()) - .setSelection(selected.toTimestamp()) - .build() - - datePicker.addOnPositiveButtonClickListener { - val date = it.toLocalDateTime().toLocalDate() - onDateSelected(date) - } - - if (!parentFragmentManager.isStateSaved) { - datePicker.show(parentFragmentManager, null) - } -} - -@Parcelize -private class CalendarDayRangeValidator( - val start: LocalDate, - val end: LocalDate, -) : CalendarConstraints.DateValidator { - - override fun isValid(dateLong: Long): Boolean { - val date = dateLong.toLocalDateTime().toLocalDate() - val daysUntilEnd = date.until(end, ChronoUnit.DAYS) - val daysUntilStart = date.until(start, ChronoUnit.DAYS) - - return daysUntilStart <= 0 && daysUntilEnd >= 0 - } -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/PendingIntentCompat.kt b/app/src/main/java/io/github/wulkanowy/utils/PendingIntentCompat.kt deleted file mode 100644 index 45ee431a..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/PendingIntentCompat.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.utils - -import android.app.PendingIntent -import android.os.Build - -object PendingIntentCompat { - - val FLAG_IMMUTABLE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - PendingIntent.FLAG_IMMUTABLE - } else 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt index 72129751..cd59b864 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt @@ -4,14 +4,12 @@ import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.db.SharedPrefProvider -import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.enums.MessageFolder import timber.log.Timber -import java.time.Duration.ofMinutes -import java.time.Instant import java.time.LocalDate +import java.time.LocalDateTime import javax.inject.Inject fun getRefreshKey(name: String, semester: Semester, start: LocalDate, end: LocalDate): String { @@ -26,8 +24,8 @@ fun getRefreshKey(name: String, student: Student): String { return "${name}_${student.userLoginId}" } -fun getRefreshKey(name: String, mailbox: Mailbox?, folder: MessageFolder): String { - return "${name}_${mailbox?.globalKey ?: "all"}_${folder.id}" +fun getRefreshKey(name: String, student: Student, folder: MessageFolder): String { + return "${name}_${student.id}_${folder.id}" } class AutoRefreshHelper @Inject constructor( @@ -35,11 +33,11 @@ class AutoRefreshHelper @Inject constructor( private val sharedPref: SharedPrefProvider ) { - fun shouldBeRefreshed(key: String): Boolean { - val timestamp = sharedPref.getLong(key, 0).let(Instant::ofEpochMilli) + fun isShouldBeRefreshed(key: String): Boolean { + val timestamp = sharedPref.getLong(key, 0).toLocalDateTime() val servicesInterval = sharedPref.getString(context.getString(R.string.pref_key_services_interval), context.getString(R.string.pref_default_services_interval)).toLong() - val shouldBeRefreshed = timestamp < Instant.now().minus(ofMinutes(servicesInterval)) + val shouldBeRefreshed = timestamp < LocalDateTime.now().minusMinutes(servicesInterval) Timber.d("Check if $key need to be refreshed: $shouldBeRefreshed (last refresh: $timestamp, interval: $servicesInterval min)") @@ -47,11 +45,6 @@ class AutoRefreshHelper @Inject constructor( } fun updateLastRefreshTimestamp(key: String) { - sharedPref.putLong(key, Instant.now().toEpochMilli()) - } - - fun getLastRefreshTimestamp(key: String): Instant { - val refreshTimestampMilli = sharedPref.getLong(key, 0) - return Instant.ofEpochMilli(refreshTimestampMilli) + sharedPref.putLong(key, LocalDateTime.now().toTimestamp()) } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/RemoteConfigDefaults.kt b/app/src/main/java/io/github/wulkanowy/utils/RemoteConfigDefaults.kt deleted file mode 100644 index 6e8f7ae6..00000000 --- a/app/src/main/java/io/github/wulkanowy/utils/RemoteConfigDefaults.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.wulkanowy.utils - -enum class RemoteConfigDefaults(val value: Any) { - USER_AGENT_TEMPLATE(""), - ; - - val key get() = name.lowercase() -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt new file mode 100644 index 00000000..da5fd3db --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.utils + +import android.content.res.Resources +import io.github.wulkanowy.R +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException +import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException +import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException +import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException +import io.github.wulkanowy.sdk.scrapper.exception.VulcanException +import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException +import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException +import okhttp3.internal.http2.StreamResetException +import java.io.InterruptedIOException +import java.net.ConnectException +import java.net.SocketTimeoutException +import java.net.UnknownHostException + +fun Resources.getString(error: Throwable) = when (error) { + is UnknownHostException -> getString(R.string.error_no_internet) + is SocketTimeoutException, is InterruptedIOException, is ConnectException, is StreamResetException -> getString(R.string.error_timeout) + is NotLoggedInException -> getString(R.string.error_login_failed) + is PasswordChangeRequiredException -> getString(R.string.error_password_change_required) + is ServiceUnavailableException -> getString(R.string.error_service_unavailable) + is FeatureDisabledException -> getString(R.string.error_feature_disabled) + is FeatureNotAvailableException -> getString(R.string.error_feature_not_available) + is VulcanException -> getString(R.string.error_unknown_uonet) + is ScrapperException -> getString(R.string.error_unknown_app) + else -> getString(R.string.error_unknown) +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/SchooldaysValidator.kt b/app/src/main/java/io/github/wulkanowy/utils/SchooldaysValidator.kt new file mode 100644 index 00000000..00fccfc8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/SchooldaysValidator.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.utils + +import com.google.android.material.datepicker.CalendarConstraints +import kotlinx.parcelize.Parcelize +import java.time.DayOfWeek +import java.time.temporal.ChronoUnit + +@Parcelize +class SchoolDaysValidator(val start: Long, val end: Long) : CalendarConstraints.DateValidator { + + override fun isValid(dateLong: Long): Boolean { + val date = dateLong.toLocalDateTime() + + return date.until(end.toLocalDateTime(), ChronoUnit.DAYS) >= 0 && + date.until(start.toLocalDateTime(), ChronoUnit.DAYS) <= 0 && + date.dayOfWeek != DayOfWeek.SUNDAY + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt index 889d64ea..63a30db8 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt @@ -12,18 +12,18 @@ fun Sdk.init(student: Student): Sdk { studentId = student.studentId classId = student.classId - if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) { - mobileBaseUrl = student.mobileBaseUrl - } else { + if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { scrapperBaseUrl = student.scrapperBaseUrl - domainSuffix = student.scrapperDomainSuffix loginType = Sdk.ScrapperLoginType.valueOf(student.loginType) } + loginId = student.userLoginId mode = Sdk.Mode.valueOf(student.loginMode) mobileBaseUrl = student.mobileBaseUrl - keyId = student.certificateKey - privatePem = student.privateKey + certKey = student.certificateKey + privateKey = student.privateKey + + emptyCookieJarInterceptor = true Timber.d("Sdk in ${student.loginMode} mode reinitialized") diff --git a/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt index 380d6bf6..6e11a8b2 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt @@ -15,8 +15,5 @@ fun List.getCurrentOrLast(): Semester { // when there is more than one current semester - find one with higher id singleOrNull { semester -> semester.semesterId == maxByOrNull { it.semesterId }?.semesterId }?.let { return it } - // when there is no active kindergarten semester - get one from last year - singleOrNull { semester -> semester.schoolYear == maxByOrNull { it.schoolYear }?.schoolYear }?.let { return it } - throw IllegalArgumentException("Duplicated last semester! Semesters: ${joinToString(separator = "\n")}") } diff --git a/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt index 8043e365..5c888f30 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt @@ -1,15 +1,9 @@ package io.github.wulkanowy.utils -import androidx.core.text.parseAsHtml -import org.apache.commons.text.StringEscapeUtils - inline fun String?.ifNullOrBlank(defaultValue: () -> String) = if (isNullOrBlank()) defaultValue() else this fun String.capitalise() = replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } -fun String.parseUonetHtml() = this - .let(StringEscapeUtils::unescapeHtml4) - .replace("\n", "
") - .parseAsHtml() +fun String.decapitalise() = replaceFirstChar { it.lowercase() } diff --git a/app/src/main/java/io/github/wulkanowy/utils/StudentExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/StudentExtension.kt index 132a3085..fdd0610a 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/StudentExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/StudentExtension.kt @@ -2,4 +2,4 @@ package io.github.wulkanowy.utils import io.github.wulkanowy.data.db.entities.Student -inline val Student.nickOrName get() = nick.ifBlank { studentName } +inline val Student.nickOrName get() = if (nick.isBlank()) studentName else nick diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt index e7a50d0c..94b6a219 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt @@ -1,34 +1,41 @@ package io.github.wulkanowy.utils import java.text.SimpleDateFormat -import java.time.* -import java.time.DayOfWeek.* +import java.time.DayOfWeek.FRIDAY +import java.time.DayOfWeek.MONDAY +import java.time.DayOfWeek.SATURDAY +import java.time.DayOfWeek.SUNDAY +import java.time.Instant +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import java.time.Month +import java.time.ZoneId +import java.time.ZoneOffset import java.time.format.DateTimeFormatter -import java.time.temporal.TemporalAdjusters.* -import java.util.* +import java.time.temporal.TemporalAdjusters.firstInMonth +import java.time.temporal.TemporalAdjusters.next +import java.time.temporal.TemporalAdjusters.previous +import java.util.Locale private const val DEFAULT_DATE_PATTERN = "dd.MM.yyyy" -fun LocalDate.toTimestamp(): Long = atStartOfDay() - .toInstant(ZoneOffset.UTC) - .toEpochMilli() - -fun Long.toLocalDateTime(): LocalDateTime = LocalDateTime.ofInstant( - Instant.ofEpochMilli(this), ZoneOffset.UTC -) - -fun Instant.toLocalDate(): LocalDate = atZone(ZoneOffset.UTC).toLocalDate() - fun String.toLocalDate(format: String = DEFAULT_DATE_PATTERN): LocalDate = LocalDate.parse(this, DateTimeFormatter.ofPattern(format)) +fun LocalDateTime.toTimestamp() = + atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toInstant().toEpochMilli() + +fun Long.toLocalDateTime(): LocalDateTime = + LocalDateTime.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault()) + +fun LocalDate.toTimestamp() = atTime(LocalTime.now()).toTimestamp() + fun LocalDate.toFormattedString(pattern: String = DEFAULT_DATE_PATTERN): String = format(DateTimeFormatter.ofPattern(pattern)) -fun Instant.toFormattedString( - pattern: String = DEFAULT_DATE_PATTERN, - tz: ZoneId = ZoneId.systemDefault() -): String = atZone(tz).format(DateTimeFormatter.ofPattern(pattern)) +fun LocalDateTime.toFormattedString(pattern: String = DEFAULT_DATE_PATTERN): String = + format(DateTimeFormatter.ofPattern(pattern)) fun Month.getFormattedName(): String { val formatter = SimpleDateFormat("LLLL", Locale.getDefault()) @@ -78,31 +85,35 @@ inline val LocalDate.previousOrSameSchoolDay: LocalDate inline val LocalDate.weekDayName: String get() = format(DateTimeFormatter.ofPattern("EEEE", Locale.getDefault())) -inline val LocalDate.monday: LocalDate get() = with(MONDAY) +inline val LocalDate.monday: LocalDate + get() = with(MONDAY) -inline val LocalDate.sunday: LocalDate get() = with(SUNDAY) +inline val LocalDate.sunday: LocalDate + get() = with(SUNDAY) /** * [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335) */ -val LocalDate.isHolidays: Boolean - get() = isBefore(firstSchoolDayInCalendarYear) && isAfter(lastSchoolDayInCalendarYear) +inline val LocalDate.isHolidays: Boolean + get() = isBefore(firstSchoolDay) && isAfter(lastSchoolDay) -val LocalDate.firstSchoolDayInSchoolYear: LocalDate - get() = withYear(if (this.monthValue <= 6) this.year - 1 else this.year).firstSchoolDayInCalendarYear - -val LocalDate.lastSchoolDayInSchoolYear: LocalDate - get() = withYear(if (this.monthValue > 6) this.year + 1 else this.year).lastSchoolDayInCalendarYear - -fun LocalDate.getLastSchoolDayIfHoliday(schoolYear: Int): LocalDate { - val date = LocalDate.of(schoolYear.getSchoolYearByMonth(monthValue), monthValue, dayOfMonth) - - if (date.isHolidays) { - return date.lastSchoolDayInCalendarYear +inline val LocalDate.firstSchoolDay: LocalDate + get() = LocalDate.of(year, 9, 1).run { + when (dayOfWeek) { + FRIDAY, SATURDAY, SUNDAY -> with(firstInMonth(MONDAY)) + else -> this + } } - return date -} +inline val LocalDate.lastSchoolDay: LocalDate + get() = LocalDate.of(year, 6, 20) + .with(next(FRIDAY)) + +inline val LocalDate.schoolYearStart: LocalDate + get() = withYear(if (this.monthValue <= 6) this.year - 1 else this.year).firstSchoolDay + +inline val LocalDate.schoolYearEnd: LocalDate + get() = withYear(if (this.monthValue > 6) this.year + 1 else this.year).lastSchoolDay private fun Int.getSchoolYearByMonth(monthValue: Int): Int { return when (monthValue) { @@ -111,15 +122,12 @@ private fun Int.getSchoolYearByMonth(monthValue: Int): Int { } } -private inline val LocalDate.firstSchoolDayInCalendarYear: LocalDate - get() = LocalDate.of(year, 9, 1).run { - when (dayOfWeek) { - FRIDAY, SATURDAY, SUNDAY -> with(firstInMonth(MONDAY)) - else -> this - } +fun LocalDate.getLastSchoolDayIfHoliday(schoolYear: Int): LocalDate { + val date = LocalDate.of(schoolYear.getSchoolYearByMonth(monthValue), monthValue, dayOfMonth) + + if (date.isHolidays) { + return date.lastSchoolDay } -private inline val LocalDate.lastSchoolDayInCalendarYear: LocalDate - get() = LocalDate.of(year, 6, 20) - .with(next(FRIDAY)) - + return date +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt index 3e94463b..f3591306 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt @@ -3,10 +3,10 @@ package io.github.wulkanowy.utils import io.github.wulkanowy.data.db.entities.Timetable import java.time.Duration import java.time.Duration.between -import java.time.Instant -import java.time.Instant.now +import java.time.LocalDateTime +import java.time.LocalDateTime.now -fun Timetable.isShowTimeUntil(previousLessonEnd: Instant?) = when { +fun Timetable.isShowTimeUntil(previousLessonEnd: LocalDateTime?) = when { !isStudentPlan -> false canceled -> false now().isAfter(start) -> false @@ -18,7 +18,7 @@ inline val Timetable.left: Duration? get() = when { canceled -> null !isStudentPlan -> null - end >= now() && start <= now() -> between(now(), end) + end.isAfter(now()) && start.isBefore(now()) -> between(now(), end) else -> null } diff --git a/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt index 700ac2f1..6a5ad880 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ViewPagerExtension.kt @@ -1,11 +1,13 @@ package io.github.wulkanowy.utils -import androidx.viewpager2.widget.ViewPager2 +import androidx.viewpager.widget.ViewPager -inline fun ViewPager2.setOnSelectPageListener(crossinline selectListener: (position: Int) -> Unit) { - registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { +inline fun ViewPager.setOnSelectPageListener(crossinline selectListener: (position: Int) -> Unit) { + addOnPageChangeListener(object : ViewPager.OnPageChangeListener { override fun onPageSelected(position: Int) { selectListener(position) } + override fun onPageScrollStateChanged(state: Int) {} + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} }) } diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt index c994ebab..74ae1932 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt @@ -2,8 +2,10 @@ package io.github.wulkanowy.utils.security +import android.annotation.TargetApi import android.content.Context import android.os.Build.VERSION.SDK_INT +import android.os.Build.VERSION_CODES.JELLY_BEAN_MR2 import android.os.Build.VERSION_CODES.M import android.security.KeyPairGeneratorSpec import android.security.keystore.KeyGenParameterSpec @@ -114,6 +116,7 @@ fun decrypt(cipherText: String): String { } } +@TargetApi(JELLY_BEAN_MR2) private fun generateKeyPair(context: Context) { (if (SDK_INT >= M) { KeyGenParameterSpec.Builder(KEY_ALIAS, PURPOSE_DECRYPT or PURPOSE_ENCRYPT) diff --git a/app/src/main/play/listings/cs-CZ/full-description.txt b/app/src/main/play/listings/cs-CZ/full-description.txt deleted file mode 100644 index 1420f5d6..00000000 --- a/app/src/main/play/listings/cs-CZ/full-description.txt +++ /dev/null @@ -1,14 +0,0 @@ -Aplikace je určena pro uživatele deníku VULCAN UONET+. - -Zvýrazněné vlastnosti a funkce: -- výpočet váženého průměru, -- procentuální zobrazení docházky, -- šťastné číslo, -- náhled na další a dokončené lekce, -- tmavý motiv, -- žádné reklamy, -- offline režim, -- upozornění. - -GitHub: https://github.com/wulkanowy/wulkanowy -Discord: https://discord.gg/vccAQBr diff --git a/app/src/main/play/listings/cs-CZ/short-description.txt b/app/src/main/play/listings/cs-CZ/short-description.txt deleted file mode 100644 index 0f29ab1b..00000000 --- a/app/src/main/play/listings/cs-CZ/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -Neoficiální aplikace žáka a rodiče pro deníku VULCAN UONET+ diff --git a/app/src/main/play/listings/cs-CZ/title.txt b/app/src/main/play/listings/cs-CZ/title.txt deleted file mode 100644 index b7f42a5b..00000000 --- a/app/src/main/play/listings/cs-CZ/title.txt +++ /dev/null @@ -1 +0,0 @@ -Wulkanowy Deníček diff --git a/app/src/main/play/listings/sk/full-description.txt b/app/src/main/play/listings/sk/full-description.txt deleted file mode 100644 index 2a4787d2..00000000 --- a/app/src/main/play/listings/sk/full-description.txt +++ /dev/null @@ -1,14 +0,0 @@ -Aplikácia je určená pre užívateľov denníka VULCAN UONET+. - -Zvýraznené vlastnosti a funkcie: -- výpočet váženého priemeru, -- percentuálne zobrazenie dochádzky, -- šťastné číslo, -- náhľad na ďalšie a dokončené lekcie, -- tmavý motív, -- žiadne reklamy, -- offline režim, -- upozornenia. - -GitHub: https://github.com/wulkanowy/wulkanowy -Discord: https://discord.gg/vccAQBr diff --git a/app/src/main/play/listings/sk/short-description.txt b/app/src/main/play/listings/sk/short-description.txt deleted file mode 100644 index 645ebbb6..00000000 --- a/app/src/main/play/listings/sk/short-description.txt +++ /dev/null @@ -1 +0,0 @@ -Neoficiálna aplikácia žiaka a rodiča pre denníka VULCAN UONET+ diff --git a/app/src/main/play/listings/sk/title.txt b/app/src/main/play/listings/sk/title.txt deleted file mode 100644 index aa50ce77..00000000 --- a/app/src/main/play/listings/sk/title.txt +++ /dev/null @@ -1 +0,0 @@ -Wulkanowy Denníček diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index e881cfda..42fd2229 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,7 +1,10 @@ -Wersja 2.0.8 +Wersja 1.2.0 -— poprawiliśmy wyświetlanie kilku rodzajów zmian w planie lekcji -— dodaliśmy limit znaków w okienku usprawiedliwiania -— naprawiliśmy wyświetlanie frekwencji w szkołach, gdzie działa już system eduOne (ciągle jednak brak opcji usprawiedliwiania) +- dodaliśmy nowy ekran startowy 🎉 +- usprawniliśmy powiadomienia +- dodaliśmy wersje robocze, filtrowanie oraz informację o odczytaniu przez odbiorcę w wiadomościach +- dodaliśmy informacje o liczeniu średniej w podsumowaniu ocen +- dodaliśmy opcję generowania wiadomości z usprawiedliwieniem dni w szkołach pozbawionych funkcji usprawiedliwiania przez zakładkę frekwencja +- oraz wiele wiele innych ulepszeń i poprawek Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases diff --git a/app/src/main/res/drawable-night/background_header_note.xml b/app/src/main/res/drawable-night/background_header_note.xml new file mode 100644 index 00000000..6b594e7c --- /dev/null +++ b/app/src/main/res/drawable-night/background_header_note.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable-v23/compat_splash_screen_no_icon_background.xml b/app/src/main/res/drawable-v23/compat_splash_screen_no_icon_background.xml deleted file mode 100644 index dad56a17..00000000 --- a/app/src/main/res/drawable-v23/compat_splash_screen_no_icon_background.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable-v23/img_splash_logo.png b/app/src/main/res/drawable-v23/img_splash_logo.png new file mode 100644 index 00000000..61489d81 Binary files /dev/null and b/app/src/main/res/drawable-v23/img_splash_logo.png differ diff --git a/app/src/main/res/drawable-v23/layer_splash_background.xml b/app/src/main/res/drawable-v23/layer_splash_background.xml new file mode 100644 index 00000000..1b4b64ec --- /dev/null +++ b/app/src/main/res/drawable-v23/layer_splash_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/background_grade_details_rounded.xml b/app/src/main/res/drawable/background_grade_details_rounded.xml deleted file mode 100644 index e24088a0..00000000 --- a/app/src/main/res/drawable/background_grade_details_rounded.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/background_grade_details_weight_rounded.xml b/app/src/main/res/drawable/background_grade_details_weight_rounded.xml deleted file mode 100644 index 4b210912..00000000 --- a/app/src/main/res/drawable/background_grade_details_weight_rounded.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/background_header_note.xml b/app/src/main/res/drawable/background_header_note.xml index 8cf84a1c..c21e55c6 100644 --- a/app/src/main/res/drawable/background_header_note.xml +++ b/app/src/main/res/drawable/background_header_note.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/background_luckynumber_widget_button.xml b/app/src/main/res/drawable/background_luckynumber_widget.xml similarity index 62% rename from app/src/main/res/drawable/background_luckynumber_widget_button.xml rename to app/src/main/res/drawable/background_luckynumber_widget.xml index 66b1685f..f29744d0 100644 --- a/app/src/main/res/drawable/background_luckynumber_widget_button.xml +++ b/app/src/main/res/drawable/background_luckynumber_widget.xml @@ -1,6 +1,6 @@ - - + + diff --git a/app/src/main/res/drawable/background_luckynumber_widget_dark.xml b/app/src/main/res/drawable/background_luckynumber_widget_dark.xml new file mode 100644 index 00000000..fa15fd85 --- /dev/null +++ b/app/src/main/res/drawable/background_luckynumber_widget_dark.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/background_material_alert_dialog.xml b/app/src/main/res/drawable/background_material_alert_dialog.xml deleted file mode 100644 index 5ab8a350..00000000 --- a/app/src/main/res/drawable/background_material_alert_dialog.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/background_timetable_widget_avatar.xml b/app/src/main/res/drawable/background_timetable_widget_avatar.xml deleted file mode 100644 index 7f64c4eb..00000000 --- a/app/src/main/res/drawable/background_timetable_widget_avatar.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/background_widget_header_timetable.xml similarity index 57% rename from app/src/main/res/drawable/ic_circle.xml rename to app/src/main/res/drawable/background_widget_header_timetable.xml index d2932fe6..98eec700 100644 --- a/app/src/main/res/drawable/ic_circle.xml +++ b/app/src/main/res/drawable/background_widget_header_timetable.xml @@ -1,10 +1,7 @@ - - + - - - + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_widget_header_timetable_dark.xml b/app/src/main/res/drawable/background_widget_header_timetable_dark.xml new file mode 100644 index 00000000..616a9127 --- /dev/null +++ b/app/src/main/res/drawable/background_widget_header_timetable_dark.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_widget_item_timetable.xml b/app/src/main/res/drawable/background_widget_item_timetable.xml index 09635758..08854fba 100644 --- a/app/src/main/res/drawable/background_widget_item_timetable.xml +++ b/app/src/main/res/drawable/background_widget_item_timetable.xml @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_grade_small_rounded.xml b/app/src/main/res/drawable/background_widget_item_timetable_dark.xml similarity index 51% rename from app/src/main/res/drawable/background_grade_small_rounded.xml rename to app/src/main/res/drawable/background_widget_item_timetable_dark.xml index dd50417f..e432a648 100644 --- a/app/src/main/res/drawable/background_grade_small_rounded.xml +++ b/app/src/main/res/drawable/background_widget_item_timetable_dark.xml @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_widget_timetable.xml b/app/src/main/res/drawable/background_widget_timetable.xml index b589ad29..2267587d 100644 --- a/app/src/main/res/drawable/background_widget_timetable.xml +++ b/app/src/main/res/drawable/background_widget_timetable.xml @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_grade_rounded.xml b/app/src/main/res/drawable/background_widget_timetable_dark.xml similarity index 74% rename from app/src/main/res/drawable/background_grade_rounded.xml rename to app/src/main/res/drawable/background_widget_timetable_dark.xml index 52c10c2f..6fe7d0ab 100644 --- a/app/src/main/res/drawable/background_grade_rounded.xml +++ b/app/src/main/res/drawable/background_widget_timetable_dark.xml @@ -1,5 +1,5 @@ - + - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_all_clock.xml b/app/src/main/res/drawable/ic_all_clock.xml deleted file mode 100644 index 4b98ed23..00000000 --- a/app/src/main/res/drawable/ic_all_clock.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_auth_success.xml b/app/src/main/res/drawable/ic_auth_success.xml deleted file mode 100644 index 015553c0..00000000 --- a/app/src/main/res/drawable/ic_auth_success.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_calendar_all.xml b/app/src/main/res/drawable/ic_calendar_all.xml deleted file mode 100644 index 5908035e..00000000 --- a/app/src/main/res/drawable/ic_calendar_all.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_chevron_left.xml b/app/src/main/res/drawable/ic_chevron_left.xml index 4250fae4..ee3ff4be 100644 --- a/app/src/main/res/drawable/ic_chevron_left.xml +++ b/app/src/main/res/drawable/ic_chevron_left.xml @@ -1,10 +1,5 @@ - - + + diff --git a/app/src/main/res/drawable/ic_chevron_right.xml b/app/src/main/res/drawable/ic_chevron_right.xml index de5037cf..a6d73497 100644 --- a/app/src/main/res/drawable/ic_chevron_right.xml +++ b/app/src/main/res/drawable/ic_chevron_right.xml @@ -1,10 +1,5 @@ - - + + diff --git a/app/src/main/res/drawable/ic_dashboard_warning.xml b/app/src/main/res/drawable/ic_dashboard_warning.xml deleted file mode 100644 index e7a5dc5a..00000000 --- a/app/src/main/res/drawable/ic_dashboard_warning.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_error_filled.xml b/app/src/main/res/drawable/ic_error_filled.xml deleted file mode 100644 index 61b575dc..00000000 --- a/app/src/main/res/drawable/ic_error_filled.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_help.xml b/app/src/main/res/drawable/ic_help.xml deleted file mode 100644 index 9c6ba292..00000000 --- a/app/src/main/res/drawable/ic_help.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_history.xml b/app/src/main/res/drawable/ic_history.xml deleted file mode 100644 index f20e2094..00000000 --- a/app/src/main/res/drawable/ic_history.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground_dev_mono.xml b/app/src/main/res/drawable/ic_launcher_foreground_dev_mono.xml deleted file mode 100644 index b1b01a0b..00000000 --- a/app/src/main/res/drawable/ic_launcher_foreground_dev_mono.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground_mono.xml b/app/src/main/res/drawable/ic_launcher_foreground_mono.xml deleted file mode 100644 index e2e74731..00000000 --- a/app/src/main/res/drawable/ic_launcher_foreground_mono.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_menu_order_drag.xml b/app/src/main/res/drawable/ic_menu_order_drag.xml deleted file mode 100644 index 623c67d2..00000000 --- a/app/src/main/res/drawable/ic_menu_order_drag.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_message_select_all.xml b/app/src/main/res/drawable/ic_message_select_all.xml deleted file mode 100644 index eab195d9..00000000 --- a/app/src/main/res/drawable/ic_message_select_all.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_message_unselect_all.xml b/app/src/main/res/drawable/ic_message_unselect_all.xml deleted file mode 100644 index c388522e..00000000 --- a/app/src/main/res/drawable/ic_message_unselect_all.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_scale_balance.xml b/app/src/main/res/drawable/ic_scale_balance.xml deleted file mode 100644 index c65467a6..00000000 --- a/app/src/main/res/drawable/ic_scale_balance.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_settings_ads.xml b/app/src/main/res/drawable/ic_settings_ads.xml deleted file mode 100644 index c333ea76..00000000 --- a/app/src/main/res/drawable/ic_settings_ads.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/drawable/ic_splash_logo.xml b/app/src/main/res/drawable/ic_splash_logo.xml deleted file mode 100644 index e2e74731..00000000 --- a/app/src/main/res/drawable/ic_splash_logo.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_timetable_widget_swap.xml b/app/src/main/res/drawable/ic_timetable_widget_swap.xml deleted file mode 100644 index 2f91489a..00000000 --- a/app/src/main/res/drawable/ic_timetable_widget_swap.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_widget_chevron.png b/app/src/main/res/drawable/ic_widget_chevron.png new file mode 100644 index 00000000..34345521 Binary files /dev/null and b/app/src/main/res/drawable/ic_widget_chevron.png differ diff --git a/app/src/main/res/drawable/ic_widget_chevron.xml b/app/src/main/res/drawable/ic_widget_chevron.xml deleted file mode 100644 index 2c88f818..00000000 --- a/app/src/main/res/drawable/ic_widget_chevron.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/img_luckynumber_widget_preview.png b/app/src/main/res/drawable/img_luckynumber_widget_preview.png index 26701855..539b0a59 100644 Binary files a/app/src/main/res/drawable/img_luckynumber_widget_preview.png and b/app/src/main/res/drawable/img_luckynumber_widget_preview.png differ diff --git a/app/src/main/res/drawable/img_splash_logo.png b/app/src/main/res/drawable/img_splash_logo.png new file mode 100644 index 00000000..fb521bf6 Binary files /dev/null and b/app/src/main/res/drawable/img_splash_logo.png differ diff --git a/app/src/main/res/drawable/img_timetable_widget_preview.png b/app/src/main/res/drawable/img_timetable_widget_preview.png index a81bcf36..84943018 100755 Binary files a/app/src/main/res/drawable/img_timetable_widget_preview.png and b/app/src/main/res/drawable/img_timetable_widget_preview.png differ diff --git a/app/src/main/res/drawable/layer_splash_background.xml b/app/src/main/res/drawable/layer_splash_background.xml new file mode 100644 index 00000000..2cf46d1d --- /dev/null +++ b/app/src/main/res/drawable/layer_splash_background.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/shape_badge.xml b/app/src/main/res/drawable/shape_badge.xml deleted file mode 100644 index 3153f592..00000000 --- a/app/src/main/res/drawable/shape_badge.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout-v31/widget_timetable_preview.xml b/app/src/main/res/layout-v31/widget_timetable_preview.xml deleted file mode 100644 index bc556f50..00000000 --- a/app/src/main/res/layout-v31/widget_timetable_preview.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 91279263..e55ea8b9 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -1,5 +1,5 @@ @@ -10,11 +10,8 @@ android:layout_height="wrap_content" android:background="@android:color/transparent" /> - - + android:layout_height="match_parent" /> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index d14de50a..11844e24 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,31 +1,24 @@ - - - - + app:contentInsetStartWithNavigation="0dp" + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toBottomOf="@id/main_toolbar" /> - - - - + android:elevation="4dp" + app:layout_constraintBottom_toTopOf="@id/sendMessageScroll" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:targetApi="lollipop" /> + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/sendMessageToolbar"> + android:layout_height="match_parent"> - - + app:layout_constraintTop_toBottomOf="@id/sendMessageToolbar" /> diff --git a/app/src/main/res/layout/dialog_account_edit.xml b/app/src/main/res/layout/dialog_account_edit.xml index 2ab4ccc6..9f617e44 100644 --- a/app/src/main/res/layout/dialog_account_edit.xml +++ b/app/src/main/res/layout/dialog_account_edit.xml @@ -1,14 +1,19 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_ads_consent.xml b/app/src/main/res/layout/dialog_ads_consent.xml deleted file mode 100644 index 118fb9c1..00000000 --- a/app/src/main/res/layout/dialog_ads_consent.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_attendance.xml b/app/src/main/res/layout/dialog_attendance.xml index d832490a..08ed9d4a 100644 --- a/app/src/main/res/layout/dialog_attendance.xml +++ b/app/src/main/res/layout/dialog_attendance.xml @@ -5,21 +5,28 @@ android:layout_height="match_parent"> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_conference.xml b/app/src/main/res/layout/dialog_conference.xml deleted file mode 100644 index 72837b81..00000000 --- a/app/src/main/res/layout/dialog_conference.xml +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_error.xml b/app/src/main/res/layout/dialog_error.xml index 98b9c8b1..a78790bc 100644 --- a/app/src/main/res/layout/dialog_error.xml +++ b/app/src/main/res/layout/dialog_error.xml @@ -4,11 +4,19 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:minWidth="300dp" - android:orientation="vertical" - tools:context=".ui.base.ErrorDialog"> + android:orientation="vertical"> + + - + + + + + + + + + + + + + + android:gravity="end" + android:minHeight="52dp" + android:orientation="horizontal"> - + - + - - - + + + + diff --git a/app/src/main/res/layout/dialog_exam.xml b/app/src/main/res/layout/dialog_exam.xml index 519d1531..51153ac8 100644 --- a/app/src/main/res/layout/dialog_exam.xml +++ b/app/src/main/res/layout/dialog_exam.xml @@ -5,21 +5,28 @@ android:layout_height="match_parent"> + + - - - - - - - - - - - - + app:layout_constraintTop_toBottomOf="@id/examDialogSubjectTitle" /> + app:layout_constraintTop_toBottomOf="@id/examDialogSubjectValue" /> + app:layout_constraintTop_toBottomOf="@id/examDialogTypeTitle" /> + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/examDialogDateValue" /> - - + android:layout_height="wrap_content"> + android:hint="@string/attendance_excuse_dialog_reason" /> diff --git a/app/src/main/res/layout/dialog_grade.xml b/app/src/main/res/layout/dialog_grade.xml index f47f6108..9ed2bc06 100644 --- a/app/src/main/res/layout/dialog_grade.xml +++ b/app/src/main/res/layout/dialog_grade.xml @@ -21,7 +21,7 @@ - - - - - - + android:background="@color/grade_black" + android:gravity="center" + android:maxLines="2" + android:minHeight="32dp" + android:textColor="@android:color/white" + android:textIsSelectable="true" + android:textSize="14sp" + tools:text="Waga: 1.00" /> + android:textIsSelectable="true" + android:textSize="21sp" + android:textStyle="bold" /> + android:layout_marginTop="8dp" + android:textIsSelectable="true" + android:textSize="16sp" /> + android:textColor="?android:textColorSecondary" + android:textSize="12sp" /> + android:textIsSelectable="true" + android:textSize="16sp" /> + android:textColor="?android:textColorSecondary" + android:textSize="12sp" /> + android:textIsSelectable="true" + android:textSize="16sp" /> + android:textColor="?android:textColorSecondary" + android:textSize="12sp" /> + android:textIsSelectable="true" + android:textSize="16sp" /> + android:textColor="?android:textColorSecondary" + android:textSize="12sp" /> + android:textIsSelectable="true" + android:textSize="16sp" /> + + + diff --git a/app/src/main/res/layout/dialog_homework.xml b/app/src/main/res/layout/dialog_homework.xml index 8c6cf0a7..22a03cb2 100644 --- a/app/src/main/res/layout/dialog_homework.xml +++ b/app/src/main/res/layout/dialog_homework.xml @@ -1,56 +1,70 @@ - + android:layout_height="match_parent" + android:orientation="vertical"> + android:layout_height="1dp" /> - + - - + + + + + diff --git a/app/src/main/res/layout/dialog_homework_add.xml b/app/src/main/res/layout/dialog_homework_add.xml deleted file mode 100644 index e0ff5b74..00000000 --- a/app/src/main/res/layout/dialog_homework_add.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_lesson_completed.xml b/app/src/main/res/layout/dialog_lesson_completed.xml index 3a1d3fd0..500cdb6f 100644 --- a/app/src/main/res/layout/dialog_lesson_completed.xml +++ b/app/src/main/res/layout/dialog_lesson_completed.xml @@ -1,5 +1,4 @@ - + + - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_mobile_device.xml b/app/src/main/res/layout/dialog_mobile_device.xml index 9b81737f..043afb2a 100644 --- a/app/src/main/res/layout/dialog_mobile_device.xml +++ b/app/src/main/res/layout/dialog_mobile_device.xml @@ -7,10 +7,18 @@ tools:context=".ui.modules.mobiledevice.token.MobileDeviceTokenDialog"> + + - + + - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_timetable.xml b/app/src/main/res/layout/dialog_timetable.xml index aeb01b3b..04a5c0c1 100644 --- a/app/src/main/res/layout/dialog_timetable.xml +++ b/app/src/main/res/layout/dialog_timetable.xml @@ -6,20 +6,28 @@ android:layout_height="match_parent"> + + + app:layout_constraintTop_toBottomOf="@id/timetableDialogLessonValue" /> + app:layout_constraintTop_toBottomOf="@id/timetableDialogTeacherValue" /> + app:layout_constraintTop_toBottomOf="@id/timetableDialogRoomValue" /> + android:layout_height="match_parent" + android:layout_marginBottom="50dp"> @@ -105,7 +105,7 @@ + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_left" /> + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_right" /> - + diff --git a/app/src/main/res/layout/fragment_attendance_summary.xml b/app/src/main/res/layout/fragment_attendance_summary.xml index 6b92e23f..613ea506 100644 --- a/app/src/main/res/layout/fragment_attendance_summary.xml +++ b/app/src/main/res/layout/fragment_attendance_summary.xml @@ -118,7 +118,7 @@ + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_left" /> + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_grade.xml b/app/src/main/res/layout/fragment_grade.xml index f76419d1..ed0447fb 100644 --- a/app/src/main/res/layout/fragment_grade.xml +++ b/app/src/main/res/layout/fragment_grade.xml @@ -11,14 +11,13 @@ android:layout_height="48dp" android:background="?colorSurface" android:visibility="invisible" - android:outlineProvider="none" app:tabMode="scrollable" app:tabSelectedTextColor="?colorPrimary" app:tabTextColor="@color/material_on_surface_emphasis_medium" tools:ignore="UnusedAttribute" tools:visibility="visible" /> - + android:layout_height="match_parent" + android:orientation="vertical"> - + android:layout_height="wrap_content" + android:background="?android:windowBackground" + android:padding="5dp" + android:visibility="visible" + app:layout_constraintTop_toTopOf="parent" + tools:ignore="UnusedAttribute" + tools:listitem="@layout/item_attendance_summary" + tools:visibility="visible"> - + android:ellipsize="middle" + android:paddingStart="10dp" + android:paddingLeft="10dp" + android:paddingTop="10dp" + android:paddingEnd="30dp" + android:paddingRight="30dp" + android:paddingBottom="10dp" + android:spinnerMode="dialog" /> + - - + - - + - + + + + + + + android:layout_marginTop="20dp" + android:gravity="center" + android:text="@string/grade_no_items" + android:textSize="20sp" /> + - + + + + + + + android:layout_marginTop="16dp" + android:gravity="center" + android:orientation="horizontal"> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + diff --git a/app/src/main/res/layout/fragment_grade_summary.xml b/app/src/main/res/layout/fragment_grade_summary.xml index de2d0f3b..a3425513 100644 --- a/app/src/main/res/layout/fragment_grade_summary.xml +++ b/app/src/main/res/layout/fragment_grade_summary.xml @@ -86,7 +86,7 @@ @@ -93,7 +91,7 @@ - - + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_left" /> + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_login_advanced.xml b/app/src/main/res/layout/fragment_login_advanced.xml index 37551dec..c7acaa70 100644 --- a/app/src/main/res/layout/fragment_login_advanced.xml +++ b/app/src/main/res/layout/fragment_login_advanced.xml @@ -97,7 +97,7 @@ @@ -185,34 +185,9 @@ tools:ignore="Deprecated,LabelFor" /> - - - - - + app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout"> - + app:layout_constraintTop_toBottomOf="@+id/loginFormHeader"> + app:layout_constraintTop_toBottomOf="@+id/loginFormUsernameLayout" + app:passwordToggleEnabled="true"> @@ -239,40 +230,15 @@ tools:ignore="Deprecated,LabelFor" /> - - - - - + app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout" /> + tools:visibility="visible"> + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + tools:itemCount="6" + tools:listitem="@layout/item_login_student_select" /> - - diff --git a/app/src/main/res/layout/fragment_login_symbol.xml b/app/src/main/res/layout/fragment_login_symbol.xml index d928d65f..f1c9be4a 100644 --- a/app/src/main/res/layout/fragment_login_symbol.xml +++ b/app/src/main/res/layout/fragment_login_symbol.xml @@ -66,7 +66,7 @@ + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_left" /> + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_menu_order.xml b/app/src/main/res/layout/fragment_menu_order.xml deleted file mode 100644 index 1773b45a..00000000 --- a/app/src/main/res/layout/fragment_menu_order.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - diff --git a/app/src/main/res/layout/fragment_message.xml b/app/src/main/res/layout/fragment_message.xml index b3dac0dd..a61f3738 100644 --- a/app/src/main/res/layout/fragment_message.xml +++ b/app/src/main/res/layout/fragment_message.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - + tools:listitem="@layout/item_message_preview" /> @@ -86,7 +84,7 @@ + android:layout_height="match_parent" /> - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_notifications_center.xml b/app/src/main/res/layout/fragment_notifications_center.xml deleted file mode 100644 index ba2f0ff4..00000000 --- a/app/src/main/res/layout/fragment_notifications_center.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_school.xml b/app/src/main/res/layout/fragment_school.xml index a7c3551d..32be61db 100644 --- a/app/src/main/res/layout/fragment_school.xml +++ b/app/src/main/res/layout/fragment_school.xml @@ -15,18 +15,19 @@ + android:layout_height="match_parent" + android:paddingStart="8dp" + android:paddingLeft="8dp" + android:paddingTop="8dp" + android:paddingEnd="12dp" + android:paddingRight="12dp" + android:paddingBottom="8dp"> + android:orientation="vertical"> - + android:layout_marginLeft="8dp" + android:layout_gravity="center_vertical" /> - + android:layout_marginLeft="8dp" + android:layout_gravity="center_vertical" /> - - + tools:visibility="visible"> + tools:visibility="visible"> + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_left" /> + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_timetable_additional.xml b/app/src/main/res/layout/fragment_timetable_additional.xml index e50fd6b5..61eb4445 100644 --- a/app/src/main/res/layout/fragment_timetable_additional.xml +++ b/app/src/main/res/layout/fragment_timetable_additional.xml @@ -1,15 +1,14 @@ - + android:layout_height="match_parent" + android:layout_marginBottom="50dp"> @@ -97,7 +94,7 @@ - - + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_left" /> + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_right" /> - + diff --git a/app/src/main/res/layout/fragment_timetable_completed.xml b/app/src/main/res/layout/fragment_timetable_completed.xml index 8d647ff6..1a890fe1 100644 --- a/app/src/main/res/layout/fragment_timetable_completed.xml +++ b/app/src/main/res/layout/fragment_timetable_completed.xml @@ -93,7 +93,7 @@ + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_left" /> + android:tint="?colorPrimary" + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/item_dashboard_account.xml b/app/src/main/res/layout/item_dashboard_account.xml index 7ae0c84d..139157c8 100644 --- a/app/src/main/res/layout/item_dashboard_account.xml +++ b/app/src/main/res/layout/item_dashboard_account.xml @@ -5,7 +5,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="12dp" - android:layout_marginVertical="6dp"> + android:layout_marginVertical="6dp" + app:cardElevation="4dp"> + tools:text="Szkoła Wulkanowego " /> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_admin_message.xml b/app/src/main/res/layout/item_dashboard_admin_message.xml deleted file mode 100644 index e12241df..00000000 --- a/app/src/main/res/layout/item_dashboard_admin_message.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_dashboard_ads.xml b/app/src/main/res/layout/item_dashboard_ads.xml deleted file mode 100644 index b75bb27e..00000000 --- a/app/src/main/res/layout/item_dashboard_ads.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/item_dashboard_announcements.xml b/app/src/main/res/layout/item_dashboard_announcements.xml index b9ddb757..19f72088 100644 --- a/app/src/main/res/layout/item_dashboard_announcements.xml +++ b/app/src/main/res/layout/item_dashboard_announcements.xml @@ -8,7 +8,8 @@ android:layout_marginVertical="6dp" android:clickable="true" android:focusable="true" - android:foreground="?selectableItemBackground"> + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_conferences.xml b/app/src/main/res/layout/item_dashboard_conferences.xml index b02b8e18..02d3edfc 100644 --- a/app/src/main/res/layout/item_dashboard_conferences.xml +++ b/app/src/main/res/layout/item_dashboard_conferences.xml @@ -8,7 +8,8 @@ android:layout_marginVertical="6dp" android:clickable="true" android:focusable="true" - android:foreground="?selectableItemBackground"> + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_exams.xml b/app/src/main/res/layout/item_dashboard_exams.xml index 84302403..9cc98d79 100644 --- a/app/src/main/res/layout/item_dashboard_exams.xml +++ b/app/src/main/res/layout/item_dashboard_exams.xml @@ -8,7 +8,8 @@ android:layout_marginVertical="6dp" android:clickable="true" android:focusable="true" - android:foreground="?selectableItemBackground"> + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_grades.xml b/app/src/main/res/layout/item_dashboard_grades.xml index 345d8a5e..5cc9ce30 100644 --- a/app/src/main/res/layout/item_dashboard_grades.xml +++ b/app/src/main/res/layout/item_dashboard_grades.xml @@ -6,7 +6,8 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="12dp" android:layout_marginVertical="6dp" - android:foreground="?selectableItemBackground"> + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_homework.xml b/app/src/main/res/layout/item_dashboard_homework.xml index b36afc57..975d66ef 100644 --- a/app/src/main/res/layout/item_dashboard_homework.xml +++ b/app/src/main/res/layout/item_dashboard_homework.xml @@ -8,7 +8,8 @@ android:layout_marginVertical="6dp" android:clickable="true" android:focusable="true" - android:foreground="?selectableItemBackground"> + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_horizontal_group.xml b/app/src/main/res/layout/item_dashboard_horizontal_group.xml index 1c9246a1..bbd8f351 100644 --- a/app/src/main/res/layout/item_dashboard_horizontal_group.xml +++ b/app/src/main/res/layout/item_dashboard_horizontal_group.xml @@ -4,15 +4,16 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingHorizontal="12dp" android:layout_marginVertical="2dp" - android:clipToPadding="false" - android:paddingHorizontal="12dp"> + android:clipToPadding="false"> - - - - + tools:text="16" /> @@ -150,8 +119,9 @@ - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_lessons.xml b/app/src/main/res/layout/item_dashboard_lessons.xml index a40f17f2..a2a92c54 100644 --- a/app/src/main/res/layout/item_dashboard_lessons.xml +++ b/app/src/main/res/layout/item_dashboard_lessons.xml @@ -5,7 +5,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="12dp" - android:layout_marginVertical="6dp"> + android:layout_marginVertical="6dp" + android:clickable="true" + android:focusable="true" + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_grade_details.xml b/app/src/main/res/layout/item_grade_details.xml index 6849e929..2f3bd2de 100644 --- a/app/src/main/res/layout/item_grade_details.xml +++ b/app/src/main/res/layout/item_grade_details.xml @@ -20,12 +20,11 @@ android:id="@+id/gradeItemValue" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="@drawable/background_grade_rounded" - android:backgroundTint="@color/grade_material_default" + android:background="@color/grade_material_default" android:gravity="center" android:maxLength="5" android:minWidth="45dp" - android:minHeight="45dp" + android:minHeight="40dp" android:textColor="@android:color/white" android:textSize="16sp" app:layout_constraintBottom_toBottomOf="@+id/gradeDetailsContainer" diff --git a/app/src/main/res/layout/item_grade_statistics_header.xml b/app/src/main/res/layout/item_grade_statistics_header.xml index cc35f606..92f522ba 100644 --- a/app/src/main/res/layout/item_grade_statistics_header.xml +++ b/app/src/main/res/layout/item_grade_statistics_header.xml @@ -1,10 +1,8 @@ + android:layout_height="wrap_content"> - + android:paddingEnd="16dp"> -