diff --git a/.github/workflows/deploy-store.yml b/.github/workflows/deploy-store.yml index 12338feff..e7ed6b49a 100644 --- a/.github/workflows/deploy-store.yml +++ b/.github/workflows/deploy-store.yml @@ -28,17 +28,15 @@ 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 }} - 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: Deploy to AppGallery @@ -62,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: @@ -69,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/LICENSE b/LICENSE index 2fb96cee8..5dd9cacf7 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2021 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 5c1e5ea71..000000000 --- a/README.cs.md +++ /dev/null @@ -1,78 +0,0 @@ -[English version of README](README.en.md) - -[Deutsche Version von README](README.de.md) - -[Polska wersja README](README.md) - -[Slovenská verzia README](README.sk.md) - -# Wulkanowy - -[![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) - -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 -* žádné reklamy - -## 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 - - -* [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 3f806e9fd..000000000 --- a/README.de.md +++ /dev/null @@ -1,74 +0,0 @@ -[Polska wersja README](README.md) - -[English version of README](README.en.md) - -# Wulkanowy - -[![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) - -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 - * Unterricht abgeschlossen - * 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 -* keine Werbung - -## 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 ein [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) das 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 1ac2a6721..3b6f5bb1b 100644 --- a/README.en.md +++ b/README.en.md @@ -1,11 +1,5 @@ [Polska wersja README](README.md) -[Deutsche Version von README](README.de.md) - -[Česká verze README](README.cs.md) - -[Slovenská verzia README](README.sk.md) - # Wulkanowy [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) diff --git a/README.md b/README.md index e7c7d4c5e..6478ae20a 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,5 @@ [English version of README](README.en.md) -[Deutsche Version von README](README.de.md) - -[Česká verze README](README.cs.md) - -[Slovenská verzia README](README.sk.md) - # Wulkanowy [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) diff --git a/README.sk.md b/README.sk.md deleted file mode 100644 index 2f3ba41dd..000000000 --- a/README.sk.md +++ /dev/null @@ -1,78 +0,0 @@ -[English version of README](README.en.md) - -[Deutsche Version von README](README.de.md) - -[Polska wersja README](README.md) - -[Česká verze README](README.cs.md) - -# Wulkanowy - -[![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) - -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 -* žiadne reklamy - -## 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 - - -* [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 3139901f3..695dc6399 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,22 +14,23 @@ apply from: 'sonarqube.gradle' apply from: 'hooks.gradle' android { - compileSdkVersion 31 + compileSdkVersion 30 defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 - targetSdkVersion 31 - versionCode 101 - versionName "1.4.3" + targetSdkVersion 30 + versionCode 94 + versionName "1.2.1" 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 { @@ -40,14 +40,6 @@ android { ] } } - - buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null" - - if (System.env.SET_BUILD_TIMESTAMP) { - buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis()) - } else { - buildConfigField "long", "BUILD_TIMESTAMP", "1486235849000" - } } sourceSets { @@ -70,14 +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 { - 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\"" } } @@ -86,38 +76,30 @@ 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"}\"" } 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 } @@ -148,61 +130,61 @@ kapt { } play { + serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf" + serviceAccountCredentials = file('key.p12') defaultToAppBundles = false - track = 'beta' - updatePriority = 1 - enabled.set(false) + track = 'production' + updatePriority = 3 } huaweiPublish { instances { hmsRelease { credentialsPath = "$rootDir/app/src/release/agconnect-credentials.json" - buildFormat = "aab" + buildFormat = "apk" deployType = "draft" } } } ext { - work_manager = "2.7.1" + work_manager = "2.5.0" android_hilt = "1.0.0" room = "2.3.0" chucker = "3.5.2" - mockk = "1.12.1" - coroutines = "1.5.2" + mockk = "1.12.0" + moshi = "1.12.0" } dependencies { - implementation "io.github.wulkanowy:sdk:1.4.3" + implementation "io.github.wulkanowy:sdk:1.2.1" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2" - implementation "androidx.core:core-ktx:1.7.0" - implementation 'androidx.core:core-splashscreen:1.0.0-alpha02' - implementation "androidx.activity:activity-ktx:1.4.0" - implementation "androidx.appcompat:appcompat:1.4.0" - implementation "androidx.fragment:fragment-ktx:1.4.0" - implementation "androidx.annotation:annotation:1.3.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.1.1" implementation "androidx.recyclerview:recyclerview:1.2.1" - implementation "androidx.viewpager2:viewpager2:1.1.0-beta01" + implementation "androidx.viewpager:viewpager:1.0.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" - implementation "androidx.constraintlayout:constraintlayout:2.1.2" + 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.3.1" + implementation "com.github.wulkanowy:material-chips-input:2.2.0" implementation "com.github.PhilJay:MPAndroidChart:v3.1.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.4.0" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation "androidx.room:room-runtime:$room" implementation "androidx.room:room-ktx:$room" @@ -216,41 +198,40 @@ 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:0.8.0" - implementation "com.squareup.okhttp3:logging-interceptor:4.9.3" + 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:1.4.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.6.0' + implementation 'com.fredporciuncula:flow-preferences:1.5.0' - playImplementation platform('com.google.firebase:firebase-bom:29.0.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.android.play:core:1.10.2' + playImplementation 'com.google.android.play:core:1.10.1' playImplementation 'com.google.android.play:core-ktx:1.8.1' - playImplementation 'com.google.android.gms:play-services-ads:20.5.0' - hmsImplementation 'com.huawei.hms:hianalytics:6.3.2.300' - hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.2.200' + hmsImplementation 'com.huawei.hms:hianalytics:6.2.0.301' + 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.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.2' testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - testImplementation 'org.robolectric:robolectric:4.7.2' + 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" diff --git a/app/key.p12.gpg b/app/key.p12.gpg new file mode 100644 index 000000000..e9b6d06eb Binary files /dev/null and b/app/key.p12.gpg differ 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 362c7f0e0..000000000 --- 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 9d008060a..000000000 --- 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 a5faa57b7..000000000 --- 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 22c0d8125..000000000 --- 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 4dc9834d2..000000000 --- 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/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index de4a80b06..a331c41f6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,7 +9,6 @@ - @@ -39,14 +38,13 @@ android:allowBackup="false" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="false" android:theme="@style/WulkanowyTheme" + android:usesCleartextTraffic="true" tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"> @@ -76,7 +74,6 @@ @@ -86,7 +83,6 @@ @@ -97,23 +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 7cdeb622a..4621c5928 100644 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt @@ -1,10 +1,12 @@ package io.github.wulkanowy +import android.annotation.SuppressLint import android.app.Application 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 @@ -39,8 +41,10 @@ class WulkanowyApp : Application(), Configuration.Provider { @Inject lateinit var analyticsHelper: AnalyticsHelper + @SuppressLint("UnsafeOptInUsageWarning") override fun onCreate() { super.onCreate() + FragmentManager.enableNewStateManager(false) initializeAppLanguage() themeManager.applyDefaultTheme() initLogging() 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 69% 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 cac3ffc23..a1c3cbbb7 100644 --- a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt @@ -2,100 +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 kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.json.Json -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.create 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) = - Sdk().apply { + fun provideSdk(chuckerCollector: ChuckerCollector, @ApplicationContext context: Context): Sdk { + return Sdk().apply { androidVersion = android.os.Build.VERSION.RELEASE buildTag = android.os.Build.MODEL 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() - - @OptIn(ExperimentalSerializationApi::class) - @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 @@ -105,6 +67,14 @@ 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 = @@ -118,9 +88,7 @@ internal class DataModule { @Singleton @Provides - fun provideJson() = Json { - ignoreUnknownKeys = true - } + fun provideMoshi() = Moshi.Builder().build() @Singleton @Provides @@ -234,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/api/AdminMessageService.kt b/app/src/main/java/io/github/wulkanowy/data/api/AdminMessageService.kt deleted file mode 100644 index 23f5af24a..000000000 --- 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 d87c66299..0dca9aa18 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 @@ -6,11 +6,11 @@ import androidx.room.Room import androidx.room.RoomDatabase import androidx.room.RoomDatabase.JournalMode.TRUNCATE import androidx.room.TypeConverters -import io.github.wulkanowy.data.db.dao.AdminMessageDao 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 @@ -23,10 +23,8 @@ 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.NotificationDao import io.github.wulkanowy.data.db.dao.RecipientDao import io.github.wulkanowy.data.db.dao.ReportingUnitDao -import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao import io.github.wulkanowy.data.db.dao.SchoolDao import io.github.wulkanowy.data.db.dao.SemesterDao import io.github.wulkanowy.data.db.dao.StudentDao @@ -36,11 +34,11 @@ 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.AdminMessage 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 @@ -53,11 +51,9 @@ 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.Notification 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.SchoolAnnouncement import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentInfo @@ -99,11 +95,6 @@ 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.Migration40 -import io.github.wulkanowy.data.db.migrations.Migration41 -import io.github.wulkanowy.data.db.migrations.Migration42 -import io.github.wulkanowy.data.db.migrations.Migration43 -import io.github.wulkanowy.data.db.migrations.Migration44 import io.github.wulkanowy.data.db.migrations.Migration5 import io.github.wulkanowy.data.db.migrations.Migration6 import io.github.wulkanowy.data.db.migrations.Migration7 @@ -143,8 +134,6 @@ import javax.inject.Singleton StudentInfo::class, TimetableHeader::class, SchoolAnnouncement::class, - Notification::class, - AdminMessage::class ], version = AppDatabase.VERSION_SCHEMA, exportSchema = true @@ -153,7 +142,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 44 + const val VERSION_SCHEMA = 39 fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( Migration2(), @@ -194,11 +183,6 @@ abstract class AppDatabase : RoomDatabase() { Migration37(), Migration38(), Migration39(), - Migration40(), - Migration41(sharedPrefProvider), - Migration42(), - Migration43(), - Migration44() ) fun newInstance( @@ -268,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 1993c4338..def0b3715 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,10 +1,9 @@ package io.github.wulkanowy.data.db import androidx.room.TypeConverter -import kotlinx.serialization.SerializationException -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json +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 @@ -14,7 +13,15 @@ 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 timestampToDate(value: Long?): LocalDate? = value?.run { @@ -44,25 +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() } } 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 4929f0469..0623d403f 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 000000000..4a9b168dd --- /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 87f4812da..000000000 --- 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 c6c255a1f..8ef3fd446 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/NotificationDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/NotificationDao.kt deleted file mode 100644 index c5ae21bc2..000000000 --- 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/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt index 3dda8a44b..0ad2ee590 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 @@ -14,39 +14,33 @@ import javax.inject.Singleton @Singleton @Dao -abstract class StudentDao { +interface StudentDao { @Insert(onConflict = ABORT) - abstract suspend fun insertAll(student: List): List + 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) + 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 + 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/entities/AdminMessage.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/AdminMessage.kt deleted file mode 100644 index 97fec69b7..000000000 --- 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 b40dd52e5..f141d5d52 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/Homework.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt index 4538cf31f..04ee1e8c6 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/Notification.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt deleted file mode 100644 index 740137f0f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt +++ /dev/null @@ -1,27 +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 java.time.LocalDateTime - -@Entity(tableName = "Notifications") -data class Notification( - - @ColumnInfo(name = "student_id") - val studentId: Long, - - val title: String, - - val content: String, - - val type: NotificationType, - - val date: LocalDateTime, - - 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 223322705..60e67d32f 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 @@ -3,9 +3,10 @@ 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( 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 29b3737bc..1bf159efd 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 @@ -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/migrations/Migration40.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration40.kt deleted file mode 100644 index 6d2795c7c..000000000 --- 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 0080e057c..000000000 --- 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.ui.modules.grade.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 3d66f301b..000000000 --- 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 68c2834d6..000000000 --- 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 7bdcab5f4..000000000 --- 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/pojos/Contributor.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/Contributor.kt index 4165b3f14..d2338c281 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 2e568e376..a79b70cd4 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 000000000..ca2749374 --- /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 0748ba647..000000000 --- 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 android.content.Intent -import io.github.wulkanowy.services.sync.notifications.NotificationType - -data class NotificationData( - val intentToStart: Intent, - val title: String, - val content: String -) - -data class GroupNotificationData( - val notificationDataList: List, - val title: String, - val content: String, - val intentToStart: Intent, - val type: NotificationType -) - 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 1b17e3bf3..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt +++ /dev/null @@ -1,52 +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.utils.AppInfo -import io.github.wulkanowy.utils.AutoRefreshHelper -import io.github.wulkanowy.utils.networkBoundResource -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 refreshHelper: AutoRefreshHelper, -) { - private val saveFetchResultMutex = Mutex() - - private val cacheKey = "admin_messages" - - suspend fun getAdminMessages(student: Student, forceRefresh: Boolean) = networkBoundResource( - mutex = saveFetchResultMutex, - query = { adminMessageDao.loadAll() }, - fetch = { adminMessageService.getAdminMessages() }, - shouldFetch = { - refreshHelper.shouldBeRefreshed(cacheKey) || forceRefresh - }, - saveFetchResult = { oldItems, newItems -> - adminMessageDao.removeOldAndSaveNew(oldItems, newItems) - refreshHelper.updateLastRefreshTimestamp(cacheKey) - }, - 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 cbaa12bd3..71b7ea94e 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,27 +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) @Suppress("BlockingMethodInNonBlockingContext") - suspend fun getAppCreators() = withContext(dispatchers.io) { - val inputStream = context.assets.open("contributors.json").buffered() - json.decodeFromStream>(inputStream) + 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 ec9198175..ffccb059e 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 @@ -14,7 +14,6 @@ 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.sync.Mutex import java.time.LocalDate import java.time.LocalDateTime @@ -33,24 +32,10 @@ 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, - 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 = { sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) .getAttendance(start.monday, end.sunday, semester.semesterId) @@ -58,39 +43,19 @@ class AttendanceRepository @Inject constructor( }, 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.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 bc1fb2343..58659914f 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 @@ -29,12 +29,12 @@ class AttendanceSummaryRepository @Inject constructor( student: Student, semester: Semester, subjectId: Int, - forceRefresh: Boolean, + forceRefresh: Boolean ) = networkBoundResource( mutex = saveFetchResultMutex, 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 = { 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 c2e5a7217..99ef56f4b 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 @@ -28,28 +28,10 @@ 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, - 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.schoolYear) .getCompletedLessons(start.monday, end.sunday) 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 e32271833..16d7c3c6c 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 @@ -35,12 +35,12 @@ class ConferenceRepository @Inject constructor( semester: Semester, forceRefresh: Boolean, notify: Boolean = false, - startDate: LocalDateTime = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC), + startDate: LocalDateTime = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC) ) = networkBoundResource( mutex = saveFetchResultMutex, 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) 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 9bdac0658..93d5a47cb 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 @@ -36,14 +36,14 @@ class ExamRepository @Inject constructor( start: LocalDate, end: LocalDate, forceRefresh: Boolean, - notify: Boolean = false, + notify: Boolean = false ) = networkBoundResource( mutex = saveFetchResultMutex, 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( 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 6c574b48a..d8417f8a9 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 @@ -37,12 +37,13 @@ class GradeRepository @Inject constructor( student: Student, semester: Semester, forceRefresh: Boolean, - notify: Boolean = false, + notify: Boolean = false ) = networkBoundResource( mutex = saveFetchResultMutex, 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) @@ -70,8 +71,8 @@ class GradeRepository @Inject constructor( newDetails: List, notify: Boolean ) { - val notifyBreakDate = oldGrades.maxByOrNull {it.date } - ?.date ?: student.registrationDate.toLocalDate() + 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 { @@ -88,7 +89,8 @@ class GradeRepository @Inject constructor( ) { 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 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 6c36f163b..9cd8e711d 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 @@ -39,19 +39,9 @@ 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, - 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.schoolYear) @@ -86,19 +76,9 @@ class GradeStatisticsRepository @Inject constructor( } ) - fun getGradesSemesterStatistics( - student: Student, - semester: Semester, - subjectName: String, - forceRefresh: Boolean, - ) = networkBoundResource( + fun getGradesSemesterStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource( mutex = semesterMutex, - 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.schoolYear) @@ -114,12 +94,10 @@ class GradeStatisticsRepository @Inject constructor( val itemsWithAverage = items.map { item -> item.copy().apply { val denominator = item.amounts.sum() - average = if (denominator == 0) "" else { - (item.amounts.mapIndexed { gradeValue, amount -> - (gradeValue + 1) * amount - }.sum().toDouble() / denominator).let { - "%.2f".format(Locale.FRANCE, it) - } + average = if (denominator == 0) "" else (item.amounts.mapIndexed { gradeValue, amount -> + (gradeValue + 1) * amount + }.sum().toDouble() / denominator).let { + "%.2f".format(Locale.FRANCE, it) } } } @@ -131,9 +109,7 @@ class GradeStatisticsRepository @Inject constructor( amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(), studentGrade = 0 ).apply { - average = itemsWithAverage.mapNotNull { - it.average.replace(",", ".").toDoubleOrNull() - }.average().let { + average = itemsWithAverage.mapNotNull { it.average.replace(",", ".").toDoubleOrNull() }.average().let { "%.2f".format(Locale.FRANCE, it) } }).reversed() @@ -142,17 +118,9 @@ class GradeStatisticsRepository @Inject constructor( } ) - fun getGradesPointsStatistics( - student: Student, - semester: Semester, - subjectName: String, - forceRefresh: Boolean, - ) = networkBoundResource( + fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource( mutex = pointsMutex, - 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.schoolYear) 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 95a375a45..23dd74c2c 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 @@ -30,19 +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, 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( @@ -61,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)) @@ -80,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 1a8cd6ea3..6d509b026 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 41e824e57..b904b7dba 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 @@ -23,17 +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, 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)) } @@ -47,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 224c69bd7..9977e1d57 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,6 +1,7 @@ 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.Resource @@ -17,6 +18,7 @@ import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED 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.data.pojos.MessageDraftJsonAdapter import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Folder import io.github.wulkanowy.sdk.pojo.SentMessage @@ -27,9 +29,6 @@ import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow 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 @@ -43,7 +42,7 @@ class MessageRepository @Inject constructor( @ApplicationContext private val context: Context, private val refreshHelper: AutoRefreshHelper, private val sharedPrefProvider: SharedPrefProvider, - private val json: Json, + private val moshi: Moshi, ) { private val saveFetchResultMutex = Mutex() @@ -52,18 +51,14 @@ class MessageRepository @Inject constructor( @Suppress("UNUSED_PARAMETER") fun getMessages( - student: Student, - semester: Semester, - folder: MessageFolder, - forceRefresh: Boolean, - notify: Boolean = false, + student: Student, semester: Semester, + folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false ): Flow>> = networkBoundResource( mutex = saveFetchResultMutex, shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( - key = getRefreshKey(cacheKey, student, folder) + it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed( + getRefreshKey(cacheKey, student, folder) ) - it.isEmpty() || forceRefresh || isExpired }, query = { messagesDb.loadAll(student.id.toInt(), folder.id) }, fetch = { @@ -82,8 +77,7 @@ class MessageRepository @Inject constructor( ) private fun getMessagesWithReadByChange( - old: List, - new: List, + old: List, new: List, setNotified: Boolean ): List { val oldMeta = old.map { Triple(it, it.readBy, it.unreadBy) } @@ -102,9 +96,7 @@ class MessageRepository @Inject constructor( } fun getMessage( - student: Student, - message: Message, - markAsRead: Boolean = false, + student: Student, message: Message, markAsRead: Boolean = false ): Flow> = networkBoundResource( shouldFetch = { checkNotNull(it, { "This message no longer exist!" }) @@ -143,10 +135,8 @@ class MessageRepository @Inject constructor( } suspend fun sendMessage( - student: Student, - subject: String, - content: String, - recipients: List, + student: Student, subject: String, content: String, + recipients: List ): SentMessage = sdk.init(student).sendMessage( subject = subject, content = content, @@ -169,9 +159,9 @@ class MessageRepository @Inject constructor( var draftMessage: MessageDraft? get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft)) - ?.let { json.decodeFromString(it) } + ?.let { MessageDraftJsonAdapter(moshi).fromJson(it) } set(value) = sharedPrefProvider.putString( context.getString(R.string.pref_key_message_send_draft), - value?.let { json.encodeToString(it) } + 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 bf17cbbc5..4b333bc6d 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 @@ -28,16 +28,9 @@ 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, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) - it.isEmpty() || forceRefresh || isExpired - }, + 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.schoolYear) 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 c1738b36e..d43cdbc0c 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 @@ -12,6 +12,7 @@ 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 @@ -27,19 +28,9 @@ 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, - 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.schoolYear) 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 fca263782..000000000 --- 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 11f38c3f4..bc8100f22 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 @@ -5,21 +5,21 @@ import android.content.SharedPreferences import androidx.core.content.edit import com.fredporciuncula.flow.preferences.FlowSharedPreferences import com.fredporciuncula.flow.preferences.Preference +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.sdk.toLocalDate import io.github.wulkanowy.ui.modules.dashboard.DashboardItem import io.github.wulkanowy.ui.modules.grade.GradeAverageMode -import io.github.wulkanowy.ui.modules.grade.GradeExpandMode 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.LocalDate import java.time.LocalDateTime import javax.inject.Inject @@ -28,12 +28,16 @@ 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() @@ -57,13 +61,8 @@ class PreferencesRepository @Inject constructor( R.bool.pref_default_grade_average_force_calc ) - val gradeExpandMode: GradeExpandMode - get() = GradeExpandMode.getByValue( - getString( - R.string.pref_key_expand_grade_mode, - R.string.pref_default_expand_grade_mode - ) - ) + val isGradeExpandable: Boolean + get() = !getBoolean(R.string.pref_key_expand_grade, R.bool.pref_default_expand_grade) val showAllSubjectsOnStatisticsList: Boolean get() = getBoolean( @@ -103,31 +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 isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug) val isDebugNotificationEnable: Boolean get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug) @@ -196,20 +176,22 @@ class PreferencesRepository @Inject constructor( ) var lasSyncDate: LocalDateTime - get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date) - .toLocalDateTime() + 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) ) } @@ -218,7 +200,6 @@ class PreferencesRepository @Inject constructor( .map { set -> set.map { DashboardItem.Tile.valueOf(it) } .plus(DashboardItem.Tile.ACCOUNT) - .plus(DashboardItem.Tile.ADMIN_MESSAGE) .toSet() } @@ -226,7 +207,6 @@ class PreferencesRepository @Inject constructor( get() = selectedDashboardTilesPreference.get() .map { DashboardItem.Tile.valueOf(it) } .plus(DashboardItem.Tile.ACCOUNT) - .plus(DashboardItem.Tile.ADMIN_MESSAGE) .toSet() set(value) { val filteredValue = value.filterNot { it == DashboardItem.Tile.ACCOUNT } @@ -245,23 +225,13 @@ 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: 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() + 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) @@ -282,9 +252,6 @@ class PreferencesRepository @Inject constructor( private fun getBoolean(id: String, default: Int) = sharedPref.getBoolean(id, context.resources.getBoolean(default)) - private fun getBoolean(id: Int, default: Boolean) = - sharedPref.getBoolean(context.getString(id), default) - private companion object { private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position" @@ -294,7 +261,5 @@ class PreferencesRepository @Inject constructor( 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_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 60e6f248f..975a30a20 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 @@ -7,8 +7,6 @@ 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 @@ -17,34 +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, 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, unit: ReportingUnit, role: Int): List { - val cached = recipientDb.loadAll(unit.studentId, unit.unitId, role) - - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) - return if (cached.isEmpty() || isExpired) { + return recipientDb.loadAll(unit.studentId, unit.unitId, role).ifEmpty { refreshRecipients(student, unit, role) + recipientDb.loadAll(unit.studentId, unit.unitId, role) - } else cached + } } suspend fun getMessageRecipients(student: Student, message: Message): List { - return sdk.init(student).getMessageRecipients(message.messageId, message.senderId) - .mapToEntities(student.studentId) + 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 5940f477b..5e1063558 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/SchoolAnnouncementRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt index b6724ed34..62d806ac2 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,6 +2,7 @@ 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.sdk.Sdk @@ -11,6 +12,7 @@ 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 @@ -28,15 +30,17 @@ class SchoolAnnouncementRepository @Inject constructor( fun getSchoolAnnouncements( student: Student, - forceRefresh: Boolean, notify: Boolean = false + forceRefresh: Boolean, + notify: Boolean = false ) = networkBoundResource( mutex = saveFetchResultMutex, shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) - it.isEmpty() || forceRefresh || isExpired + it.isEmpty() || forceRefresh + || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student)) }, query = { - schoolAnnouncementDb.loadAll(student.studentId) + schoolAnnouncementDb.loadAll( + student.studentId) }, fetch = { sdk.init(student) @@ -53,11 +57,9 @@ class SchoolAnnouncementRepository @Inject constructor( refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) } ) - fun getSchoolAnnouncementFromDatabase(student: Student): Flow> { 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 288a1fb67..8b59cb589 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 @@ -5,8 +5,6 @@ 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.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 @@ -16,41 +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, - 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.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 cc954558f..4336877a5 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 @@ -26,7 +26,7 @@ class SemesterRepository @Inject constructor( student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false - ) = withContext(dispatchers.io) { + ) = withContext(dispatchers.backgroundThread) { val semesters = semesterDb.loadAll(student.studentId, student.classId) if (isShouldFetch(student, semesters, forceRefresh, refreshOnNoCurrent)) { @@ -64,7 +64,7 @@ class SemesterRepository @Inject constructor( } suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = - withContext(dispatchers.io) { + 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 e98daedf2..de66ad20f 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 @@ -19,27 +19,24 @@ class StudentInfoRepository @Inject constructor( private val saveFetchResultMutex = Mutex() - 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)) + 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 9e4a1aabc..c2f364b3d 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,9 +1,7 @@ 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.Student @@ -27,8 +25,7 @@ class StudentRepository @Inject constructor( private val studentDb: StudentDao, private val semesterDb: SemesterDao, private val sdk: Sdk, - private val appInfo: AppInfo, - private val appDatabase: AppDatabase + private val appInfo: AppInfo ) { suspend fun isStudentSaved() = getSavedStudents(false).isNotEmpty() @@ -66,7 +63,7 @@ class StudentRepository @Inject constructor( .map { it.apply { if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { - student.password = withContext(dispatchers.io) { + student.password = withContext(dispatchers.backgroundThread) { decrypt(student.password) } } @@ -77,7 +74,7 @@ class StudentRepository @Inject constructor( val student = studentDb.loadById(id) ?: throw NoCurrentStudentException() if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { - student.password = withContext(dispatchers.io) { + student.password = withContext(dispatchers.backgroundThread) { decrypt(student.password) } } @@ -88,40 +85,35 @@ class StudentRepository @Inject constructor( val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException() if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { - student.password = withContext(dispatchers.io) { + 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.API) { - password = withContext(dispatchers.io) { + 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) 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 d81cb7c92..b4bfef188 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 @@ -5,8 +5,6 @@ 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.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 @@ -17,24 +15,14 @@ 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, - 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.schoolYear) @@ -43,8 +31,6 @@ class SubjectRepository @Inject constructor( 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 029b2707a..7135edbe9 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 @@ -5,8 +5,6 @@ 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.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 @@ -17,24 +15,14 @@ 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, - 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.schoolYear) @@ -44,8 +32,6 @@ class TeacherRepository @Inject constructor( 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 8be621122..5495d0778 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 @@ -41,23 +41,18 @@ class TimetableRepository @Inject constructor( private val cacheKey = "timetable" fun getTimetable( - student: Student, - semester: Semester, - start: LocalDate, - end: LocalDate, - forceRefresh: Boolean, - refreshAdditional: Boolean = false, - notify: Boolean = false + student: Student, semester: Semester, start: LocalDate, end: LocalDate, + forceRefresh: Boolean, refreshAdditional: Boolean = false ) = networkBoundResource( mutex = saveFetchResultMutex, 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 = { @@ -68,7 +63,7 @@ class TimetableRepository @Inject constructor( 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) @@ -84,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, @@ -118,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) 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 1729f1006..cdf0c26a9 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 b388d2ac5..406d91f5f 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,6 +1,7 @@ package io.github.wulkanowy.services.alarm import android.app.PendingIntent +import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.content.Context import android.content.Intent import android.os.Build @@ -10,13 +11,11 @@ import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository 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 @@ -33,15 +32,12 @@ 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 - const val NOTIFICATION_ID = 2137 + const val NOTIFICATION_ID = "id" const val STUDENT_NAME = "student_name" const val STUDENT_ID = "student_id" @@ -71,10 +67,10 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() { 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) @@ -91,58 +87,33 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() { Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: ${start.toLocalDateTime()}, student: $studentId") - showNotification( - context, isPersistent, studentName, + 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("()") - ) - } + 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().also { - it.setSummaryText(studentName) - it.addLine(next) - }) - .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 dc9b8f1da..98bd93eb8 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,13 +25,12 @@ 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.LocalDate import java.time.LocalDateTime import java.time.LocalDateTime.now import javax.inject.Inject @@ -54,17 +53,14 @@ class TimetableNotificationSchedulerHelper @Inject constructor( 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") } @@ -73,37 +69,20 @@ class TimetableNotificationSchedulerHelper @Inject constructor( 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 ) } } @@ -164,30 +143,17 @@ class TimetableNotificationSchedulerHelper @Inject constructor( notificationType: Int, time: LocalDateTime ) { - try { - AlarmManagerCompat.setExactAndAllowWhileIdle( - alarmManager, RTC_WAKEUP, time.toTimestamp(), - 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 c7df2dbc1..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/piggyback/VulcanNotificationListenerService.kt +++ /dev/null @@ -1,24 +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() - } - } -} \ 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 4ad9ac120..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt +++ /dev/null @@ -1,90 +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) { - - private val destinations = mapOf( - "grade" to Destination.Grade, - "attendance" to Destination.Attendance, - "exam" to Destination.Exam, - "timetable" to Destination.Timetable() - ) - - init { - initializeShortcuts() - } - - fun getDestination(intent: Intent) = - destinations[intent.getStringExtra(EXTRA_SHORTCUT_DESTINATION_ID)] - - private 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) - .apply { - action = Intent.ACTION_VIEW - putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "grade") - } - ) - .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) - .apply { - action = Intent.ACTION_VIEW - putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "attendance") - } - ) - .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) - .apply { - action = Intent.ACTION_VIEW - putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "exam") - } - ) - .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) - .apply { - action = Intent.ACTION_VIEW - putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "timetable") - } - ) - .build() - ) - - shortcutsInfo.forEach { ShortcutManagerCompat.pushDynamicShortcut(context, it) } - } - - private companion object { - - private const val EXTRA_SHORTCUT_DESTINATION_ID = "shortcut_destination_id" - } -} \ No newline at end of file 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 32ca20afc..b94d97e33 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 @@ -57,24 +57,18 @@ 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) REPLACE 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()) } } - fun startOneTimeSyncWorker(): Flow { + fun startOneTimeSyncWorker(): Flow { val work = OneTimeWorkRequestBuilder() .setInputData( Data.Builder() @@ -83,11 +77,7 @@ class SyncManager @Inject constructor( ) .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 52979e635..ea1f79cbf 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.LocalDateTime +import java.time.ZoneId import kotlin.random.Random @HiltWorker @@ -34,14 +34,13 @@ 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() = 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 = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student, true) @@ -51,12 +50,12 @@ class SyncWorker @AssistedInject constructor( Timber.i("${work::class.java.simpleName} is starting") 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 } @@ -71,16 +70,13 @@ class SyncWorker @AssistedInject constructor( ) } exceptions.isNotEmpty() -> Result.retry() - else -> { - preferencesRepository.lasSyncDate = LocalDateTime.now() - Result.success() - } + else -> Result.success() } if (preferencesRepository.isDebugNotificationEnable) notify(result) Timber.i("SyncWorker result: $result") - return@withContext result + 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 3110099e5..000000000 --- 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 10dd3e004..000000000 --- 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 e276df0ca..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt +++ /dev/null @@ -1,180 +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.utils.PendingIntentCompat -import io.github.wulkanowy.utils.getCompatBitmap -import io.github.wulkanowy.utils.getCompatColor -import io.github.wulkanowy.utils.nickOrName -import java.time.LocalDateTime -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(), - notificationData.intentToStart, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) - ) - .setContentTitle(notificationData.title) - .setContentText(notificationData.content) - .setStyle( - NotificationCompat.BigTextStyle() - .bigText(notificationData.content) - .also { builder -> - if (shouldShowStudentName()) { - 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.group ?: return - 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(), - notificationData.intentToStart, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) - ) - .setContentTitle(notificationData.title) - .setContentText(notificationData.content) - .setStyle( - NotificationCompat.BigTextStyle() - .bigText(notificationData.content) - .also { builder -> - if (shouldShowStudentName()) { - 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 (shouldShowStudentName()) { - builder.setSummaryText(student.nickOrName) - } - groupNotificationData.notificationDataList.forEach { - builder.addLine(it.content) - } - } - ) - .setContentIntent( - PendingIntent.getActivity( - context, - Random.nextInt(), - groupNotificationData.intentToStart, - 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, - type = notificationType, - date = LocalDateTime.now() - ) - - notificationRepository.saveNotification(notificationEntity) - } - - private suspend fun shouldShowStudentName(): Boolean = - studentRepository.getSavedStudents(decryptPass = false).size > 1 -} 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 000000000..8c9cb471d --- /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 7bfef96a4..000000000 --- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt +++ /dev/null @@ -1,124 +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.ui.modules.splash.SplashActivity -import io.github.wulkanowy.utils.getPlural -import io.github.wulkanowy.utils.toFormattedString -import java.time.LocalDate -import java.time.LocalDateTime -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 = LocalDateTime.now() - val changedLessons = items.filter { (it.canceled || it.changes) && it.start > currentTime } - val notificationDataList = changedLessons.groupBy { it.date } - .map { (date, lessons) -> - getNotificationContents(date, lessons).map { - NotificationData( - title = context.getPlural( - R.plurals.timetable_notify_new_items_title, - 1 - ), - content = it, - intentToStart = SplashActivity.getStartIntent( - context = context, - destination = Destination.Timetable(date) - ) - ) - } - } - .flatten() - .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 - ), - intentToStart = SplashActivity.getStartIntent(context, Destination.Timetable()), - 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 c78dcd053..000000000 --- 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.ui.modules.splash.SplashActivity -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 description = context.getString(it.descriptionRes) - "${it.date.toFormattedString("dd.MM")} - ${it.subject}: $description" - } - .ifEmpty { return } - - val notificationDataList = lines.map { - NotificationData( - title = context.getPlural(R.plurals.attendance_notify_new_items_title, 1), - content = it, - intentToStart = SplashActivity.getStartIntent(context, 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 - ), - intentToStart = SplashActivity.getStartIntent(context, 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 8ef147886..fda2922f9 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.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) { + 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 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, - intentToStart = SplashActivity.getStartIntent(context, 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 - ), - intentToStart = SplashActivity.getStartIntent(context, 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 b3cf04c41..d493c4d2e 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, - intentToStart = SplashActivity.getStartIntent(context, 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 - ), - intentToStart = SplashActivity.getStartIntent(context, 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 39ecbe33d..415ba3434 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,88 +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 = "${it.subject}: ${it.entry}", - intentToStart = SplashActivity.getStartIntent(context, 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), - intentToStart = SplashActivity.getStartIntent(context, 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}", - intentToStart = SplashActivity.getStartIntent(context, 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 - ), - intentToStart = SplashActivity.getStartIntent(context, 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}", - intentToStart = SplashActivity.getStartIntent(context, 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 - ), - intentToStart = SplashActivity.getStartIntent(context, 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 ff32aa66f..fe973cade 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, - intentToStart = SplashActivity.getStartIntent(context, 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 - ), - intentToStart = SplashActivity.getStartIntent(context, 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 5c36a06c0..95156c451 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() - ), - intentToStart = SplashActivity.getStartIntent(context, 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 b98d34668..fc3641983 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,39 +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.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 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.sender}: ${it.subject}", - intentToStart = SplashActivity.getStartIntent(context, 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), - intentToStart = SplashActivity.getStartIntent(context, 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 65520e01b..f355341b7 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}", - intentToStart = SplashActivity.getStartIntent(context, Destination.Note), - ) - } - - val groupNotificationData = GroupNotificationData( - notificationDataList = notificationDataList, - intentToStart = SplashActivity.getStartIntent(context, 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 6b839d298..b38e4f609 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,54 +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.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( - intentToStart = SplashActivity.getStartIntent( - context = context, - destination = Destination.SchoolAnnouncement - ), - title = context.getPlural( - R.plurals.school_announcement_notify_new_item_title, - 1 - ), - content = "${it.subject}: ${it.content}" - ) - } - val groupNotificationData = GroupNotificationData( + fun notify(items: List, student: Student) { + val notification = MultipleNotifications( type = NotificationType.NEW_ANNOUNCEMENT, - intentToStart = SplashActivity.getStartIntent( - context = context, - 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 af79fcd2a..c3df1960f 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,77 +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 group: String?, - val channel: String, - val icon: Int -) { - NEW_CONFERENCE( - group = "new_conferences_group", - channel = NewConferencesChannel.CHANNEL_ID, - icon = R.drawable.ic_more_conferences, - ), - NEW_EXAM( - group = "new_exam_group", - channel = NewExamChannel.CHANNEL_ID, - icon = R.drawable.ic_main_exam - ), - NEW_GRADE_DETAILS( - group = "new_grade_details_group", - channel = NewGradesChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_grade, - ), - NEW_GRADE_PREDICTED( - group = "new_grade_predicted_group", - channel = NewGradesChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_grade, - ), - NEW_GRADE_FINAL( - group = "new_grade_final_group", - channel = NewGradesChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_grade, - ), - NEW_HOMEWORK( - group = "new_homework_group", - channel = NewHomeworkChannel.CHANNEL_ID, - icon = R.drawable.ic_more_homework, - ), - NEW_LUCKY_NUMBER( - group = null, - channel = LuckyNumberChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_luckynumber, - ), - NEW_MESSAGE( - group = "new_message_group", - channel = NewMessagesChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_message, - ), - NEW_NOTE( - group = "new_notes_group", - channel = NewNotesChannel.CHANNEL_ID, - icon = R.drawable.ic_stat_note - ), - NEW_ANNOUNCEMENT( - group = "new_school_announcements_group", - channel = NewSchoolAnnouncementsChannel.CHANNEL_ID, - icon = R.drawable.ic_all_about - ), - CHANGE_TIMETABLE( - group = "change_timetable_group", - channel = TimetableChangeChannel.CHANNEL_ID, - icon = R.drawable.ic_main_timetable - ), - NEW_ATTENDANCE( - group = "new_attendance_group", - channel = NewAttendanceChannel.CHANNEL_ID, - icon = R.drawable.ic_main_attendance - ), - PUSH( - group = null, - 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/AttendanceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt index f7b680e31..788e4ea2c 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,40 +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.repositories.PreferencesRepository -import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification -import io.github.wulkanowy.utils.previousOrSameSchoolDay +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 AttendanceWork @Inject constructor( - private val attendanceRepository: AttendanceRepository, - private val newAttendanceNotification: NewAttendanceNotification, - private val preferencesRepository: PreferencesRepository -) : Work { +class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work { override suspend fun doWork(student: Student, semester: Semester) { - attendanceRepository.getAttendance( - student = student, - semester = semester, - start = now().previousOrSameSchoolDay, - end = now().previousOrSameSchoolDay, - forceRefresh = true, - notify = preferencesRepository.isNotificationsEnable - ) - .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 - }) - } + attendanceRepository.getAttendance(student, semester, now().monday, now().sunday, true).waitForResult() } } 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 2a5d2d7c0..da2dcc7fe 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 @@ -5,7 +5,8 @@ import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.HomeworkRepository 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 @@ -21,13 +22,13 @@ class HomeworkWork @Inject constructor( homeworkRepository.getHomework( student = student, semester = semester, - start = now().nextOrSameSchoolDay, - end = now().nextOrSameSchoolDay, + start = now().monday, + end = now().sunday, forceRefresh = true, 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/TimetableWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt index fcc330638..2df2c9dcb 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 @@ -2,41 +2,18 @@ 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.TimetableRepository -import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification -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 TimetableWork @Inject constructor( - private val timetableRepository: TimetableRepository, - private val changeTimetableNotification: ChangeTimetableNotification, - private val preferencesRepository: PreferencesRepository + private val timetableRepository: TimetableRepository ) : Work { override suspend fun doWork(student: Student, semester: Semester) { - timetableRepository.getTimetable( - student = student, - semester = semester, - start = now().nextOrSameSchoolDay, - end = now().nextOrSameSchoolDay, - forceRefresh = true, - notify = preferencesRepository.isNotificationsEnable - ) - .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 - }) - } + timetableRepository.getTimetable(student, semester, now().monday, now().sunday, true).waitForResult() } } 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 075557a5c..0521b4a08 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,11 +1,14 @@ 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.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar.LENGTH_LONG @@ -37,6 +40,7 @@ abstract class BaseActivity, VB : ViewBinding> : themeManager.applyActivityTheme(this) super.onCreate(savedInstanceState) supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true) + AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) @Suppress("DEPRECATION") setTaskDescription( @@ -79,8 +83,8 @@ abstract class BaseActivity, VB : ViewBinding> : } 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/BaseFragmentPagerAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragmentPagerAdapter.kt index 6bca87f15..bd735535d 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 5cd5d0109..6f363bfc4 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 @@ -6,27 +6,29 @@ 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.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() - 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 @@ -62,22 +64,22 @@ open class BasePresenter( } 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/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt index 4c279d816..4ce977709 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 @@ -11,7 +11,6 @@ import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.appcompat.app.AlertDialog import androidx.core.content.getSystemService -import androidx.core.view.isGone import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.databinding.DialogErrorBinding @@ -25,6 +24,8 @@ 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 @@ -63,26 +64,26 @@ class ErrorDialog : BaseDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val errorStacktrace = error.stackTraceToString() + val stringWriter = StringWriter().apply { + error.printStackTrace(PrintWriter(this)) + } with(binding) { - errorDialogContent.text = errorStacktrace.replace(": ${error.localizedMessage}", "") + errorDialogContent.text = stringWriter.toString() with(errorDialogHorizontalScroll) { post { fullScroll(HorizontalScrollView.FOCUS_LEFT) } } errorDialogCopy.setOnClickListener { - val clip = ClipData.newPlainText("Error details", errorStacktrace) + 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(errorStacktrace) } + openConfirmDialog { openEmailClient(stringWriter.toString()) } } - errorDialogHumanizedMessage.text = resources.getString(error) - errorDialogErrorMessage.text = error.localizedMessage - errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank() + errorDialogMessage.text = resources.getString(error) errorDialogReport.isEnabled = when (error) { is UnknownHostException, is InterruptedIOException, 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 fbc994e2c..7c32ef184 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,7 +1,6 @@ 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.login.BadCredentialsException import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException @@ -10,7 +9,7 @@ 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 = { _, _ -> } @@ -26,7 +25,7 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co } protected open fun proceed(error: Throwable) { - showErrorMessage(context.resources.getString(error), error) + showErrorMessage(resources.getString(error), error) when (error) { is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl) is ScramblerException, is BadCredentialsException -> onSessionExpired() 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 e934a1822..b560ed2e7 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 @@ -41,15 +41,14 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer ) } - private fun isThemeApplicable(activity: AppCompatActivity) = - activity.packageManager + private fun isThemeApplicable(activity: AppCompatActivity): Boolean { + return activity.packageManager .getPackageInfo(activity.packageName, GET_ACTIVITIES) - .activities - .singleOrNull { it.name == activity::class.java.canonicalName } - ?.theme - .let { + .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 } + } } 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 43d4b5f9f..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt +++ /dev/null @@ -1,136 +0,0 @@ -package io.github.wulkanowy.ui.modules - -import androidx.fragment.app.Fragment -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.schoolandteachers.school.SchoolFragment -import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment -import io.github.wulkanowy.ui.modules.timetable.TimetableFragment -import java.io.Serializable -import java.time.LocalDate - -sealed interface Destination : Serializable { - - /* - 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 - */ - val type: Type - - val fragment: 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(School), - LUCKY_NUMBER(More), - MORE(More), - MESSAGE(Message); - } - - object Dashboard : Destination { - - override val type get() = Type.DASHBOARD - - override val fragment get() = DashboardFragment.newInstance() - } - - object Grade : Destination { - - override val type get() = Type.GRADE - - override val fragment get() = GradeFragment.newInstance() - } - - object Attendance : Destination { - - override val type get() = Type.ATTENDANCE - - override val fragment get() = AttendanceFragment.newInstance() - } - - object Exam : Destination { - - override val type get() = Type.EXAM - - override val fragment get() = ExamFragment.newInstance() - } - - data class Timetable(val date: LocalDate? = null) : Destination { - - override val type get() = Type.TIMETABLE - - override val fragment get() = TimetableFragment.newInstance(date) - } - - object Homework : Destination { - - override val type get() = Type.HOMEWORK - - override val fragment get() = HomeworkFragment.newInstance() - } - - object Note : Destination { - - override val type get() = Type.NOTE - - override val fragment get() = NoteFragment.newInstance() - } - - object Conference : Destination { - - override val type get() = Type.CONFERENCE - - override val fragment get() = ConferenceFragment.newInstance() - } - - object SchoolAnnouncement : Destination { - - override val type get() = Type.SCHOOL_ANNOUNCEMENT - - override val fragment get() = SchoolAnnouncementFragment.newInstance() - } - - object School : Destination { - - override val type get() = Type.SCHOOL - - override val fragment get() = SchoolFragment.newInstance() - } - - object LuckyNumber : Destination { - - override val type get() = Type.LUCKY_NUMBER - - override val fragment get() = LuckyNumberFragment.newInstance() - } - - object More : Destination { - - override val type get() = Type.MORE - - override val fragment get() = MoreFragment.newInstance() - } - - object Message : Destination { - - override val type get() = Type.MESSAGE - - override val fragment get() = MessageFragment.newInstance() - } -} 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 552749349..6bcf5f77b 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/license/LicensePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt index 5368cc19d..cc430fc2c 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 @@ -31,7 +31,7 @@ class LicensePresenter @Inject constructor( private fun loadData() { flowWithResource { - withContext(dispatchers.io) { + withContext(dispatchers.backgroundThread) { view?.appLibraries.orEmpty() } }.onEach { 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 c3137ec58..a9890ad9a 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 @@ -121,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 1f44cbbc3..d4cba580a 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 @@ -119,7 +119,7 @@ class AccountDetailsPresenter @Inject constructor( } } }.afterLoading { - view?.popViewToMain() + view?.popView() }.launch("switch") } @@ -152,14 +152,11 @@ class AccountDetailsPresenter @Inject constructor( 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") } } Status.ERROR -> { @@ -168,11 +165,7 @@ class AccountDetailsPresenter @Inject constructor( } } }.afterLoading { - if (studentWithSemesters?.student?.isCurrent == true) { - view?.popViewToMain() - } else { - view?.popViewToAccounts() - } + view?.popView() }.launch("logout") } 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 aeb743fa5..652f0c1aa 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 66e39fc72..ab6eec417 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/attendance/AttendanceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt index 5d5ed504c..6cee23963 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 @@ -36,7 +36,7 @@ class AttendanceAdapter @Inject constructor() : with(holder.binding) { attendanceItemNumber.text = item.number.toString() attendanceItemSubject.text = item.subject - attendanceItemDescription.setText(item.descriptionRes) + attendanceItemDescription.setText(item.description) attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE } attendanceItemNumber.visibility = View.GONE attendanceItemExcuseInfo.visibility = View.GONE @@ -46,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 9b5c63e4c..d816d8f00 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 @@ -7,7 +7,7 @@ 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.utils.descriptionRes +import io.github.wulkanowy.utils.description import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.toFormattedString @@ -45,7 +45,7 @@ class AttendanceDialog : DialogFragment() { 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 bd9a66926..058946796 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 @@ -12,7 +12,6 @@ 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.datepicker.CalendarConstraints import com.google.android.material.datepicker.MaterialDatePicker @@ -122,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() } @@ -133,7 +134,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() } - attendanceNavContainer.elevation = requireContext().dpToPx(8f) + attendanceNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -217,7 +218,7 @@ 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) { @@ -244,9 +245,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth) } - if (!parentFragmentManager.isStateSaved) { - datePicker.show(parentFragmentManager, null) - } + datePicker.show(this@AttendanceFragment.parentFragmentManager, null) } override fun showExcuseDialog() { @@ -288,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 54d29bcf8..9a1598128 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 @@ -152,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() }, @@ -174,8 +172,6 @@ class AttendancePresenter @Inject constructor( view?.apply { showExcuseCheckboxes(true) showExcuseButton(false) - enableSwipe(false) - showDayNavigation(false) } attendanceToExcuseList.clear() return true @@ -185,8 +181,6 @@ class AttendancePresenter @Inject constructor( view?.apply { showExcuseCheckboxes(false) showExcuseButton(true) - enableSwipe(true) - showDayNavigation(true) } } @@ -240,7 +234,6 @@ class AttendancePresenter @Inject constructor( enableSwipe(true) showRefresh(true) showProgress(false) - showErrorView(false) showEmpty(filteredAttendance.isEmpty()) showContent(filteredAttendance.isNotEmpty()) updateData(filteredAttendance.sortedBy { item -> item.number }) @@ -263,8 +256,9 @@ class AttendancePresenter @Inject constructor( showEmpty(filteredAttendance.isEmpty()) showErrorView(false) showContent(filteredAttendance.isNotEmpty()) - val anyExcusables = filteredAttendance.any { it.isExcusableOrNotExcused } - showExcuseButton(anyExcusables && (isParent || isVulcanExcusedFunctionEnabled)) + showExcuseButton(filteredAttendance.any { item -> + (!isParent && isVulcanExcusedFunctionEnabled) || (isParent && item.isExcusableOrNotExcused) + }) } analytics.logEvent( "load_data", 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 7ddd75f48..738f2ff86 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 @@ -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 dd1644a98..118971e62 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,7 +71,7 @@ class AttendanceSummaryFragment : setOnItemSelectedListener { presenter.onSubjectSelected(it?.text?.toString()) } } - binding.attendanceSummarySubjectsContainer.elevation = requireContext().dpToPx(1f) + binding.attendanceSummarySubjectsContainer.setElevationCompat(requireContext().dpToPx(1f)) } override fun updateSubjects(data: ArrayList) { 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 8b603837b..e53cda749 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 @@ -82,13 +82,7 @@ class AttendanceSummaryPresenter @Inject constructor( flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) - - attendanceSummaryRepository.getAttendanceSummary( - student = student, - semester = semester, - subjectId = subjectId, - forceRefresh = forceRefresh - ) + attendanceSummaryRepository.getAttendanceSummary(student, semester, subjectId, forceRefresh) }.onEach { when (it.status) { Status.LOADING -> { @@ -98,7 +92,6 @@ class AttendanceSummaryPresenter @Inject constructor( showRefresh(true) showProgress(false) showContent(true) - showErrorView(false) updateDataSet(sortItems(it.data)) } } @@ -106,7 +99,6 @@ class AttendanceSummaryPresenter @Inject constructor( Status.SUCCESS -> { Timber.i("Loading attendance summary result: Success") view?.apply { - showErrorView(false) showEmpty(it.data!!.isEmpty()) showContent(it.data.isNotEmpty()) updateDataSet(sortItems(it.data)) 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 f63b293cf..c87286149 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 477b762b9..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.github.wulkanowy.ui.modules.conference - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.data.db.entities.Conference -import io.github.wulkanowy.databinding.DialogConferenceBinding -import io.github.wulkanowy.utils.lifecycleAwareVariable -import io.github.wulkanowy.utils.toFormattedString - -class ConferenceDialog : DialogFragment() { - - private var binding: DialogConferenceBinding by lifecycleAwareVariable() - - private lateinit var conference: Conference - - companion object { - - private const val ARGUMENT_KEY = "item" - - fun newInstance(conference: Conference) = ConferenceDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, conference) } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.let { - conference = it.getSerializable(ARGUMENT_KEY) as Conference - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = DialogConferenceBinding.inflate(inflater).also { binding = it }.root - - 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() - } - } -} \ No newline at end of file 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 b9642b1c7..dd10a65e0 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 @@ -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,10 +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 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 dab170daa..cc7e50db5 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,7 +1,6 @@ package io.github.wulkanowy.ui.modules.conference import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.db.entities.Conference import io.github.wulkanowy.data.repositories.ConferenceRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository @@ -44,10 +43,6 @@ class ConferencePresenter @Inject constructor( loadData(true) } - fun onItemSelected(conference: Conference) { - view?.openConferenceDialog(conference) - } - fun onDetailsClick() { view?.showErrorDetailsDialog(lastError) } 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 4f73394df..f3d1b3b3f 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,6 +26,4 @@ interface ConferenceView : BaseView { fun enableSwipe(enable: Boolean) fun showContent(show: Boolean) - - fun openConferenceDialog(conference: Conference) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt index 1c09c2a74..56503d027 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt @@ -1,8 +1,6 @@ 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 @@ -16,12 +14,10 @@ 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.databinding.ItemDashboardAccountBinding -import io.github.wulkanowy.databinding.ItemDashboardAdminMessageBinding import io.github.wulkanowy.databinding.ItemDashboardAnnouncementsBinding import io.github.wulkanowy.databinding.ItemDashboardConferencesBinding import io.github.wulkanowy.databinding.ItemDashboardExamsBinding @@ -35,7 +31,6 @@ 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 timber.log.Timber import java.time.Duration import java.time.LocalDate import java.time.LocalDateTime @@ -57,7 +52,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter Unit = {} - var onLessonsTileClickListener: (LocalDate) -> Unit = {} + var onLessonsTileClickListener: () -> Unit = {} var onHomeworkTileClickListener: () -> Unit = {} @@ -67,10 +62,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter Unit = {} - var onAdminMessageClickListener: (String?) -> Unit = {} - - var onAdminMessageDismissClickListener: (AdminMessage) -> Unit = {} - val items = mutableListOf() fun submitList(newItems: List) { @@ -117,9 +108,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter ConferencesViewHolder( ItemDashboardConferencesBinding.inflate(inflater, parent, false) ) - DashboardItem.Type.ADMIN_MESSAGE.ordinal -> AdminMessageViewHolder( - ItemDashboardAdminMessageBinding.inflate(inflater, parent, false) - ) else -> throw IllegalArgumentException() } } @@ -134,7 +122,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) } } @@ -183,8 +170,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { context.getThemeAttrColor(R.attr.colorOnSurface) @@ -214,12 +199,13 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { @@ -234,8 +220,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { matchConstraintPercentWidth = when { luckyNumber == null && unreadMessagesCount == null -> 1.0f @@ -247,8 +232,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 } 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) } } @@ -351,7 +322,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { - context.getThemeAttrColor(R.attr.colorPrimary) to - context.getThemeAttrColor(R.attr.colorOnPrimary) - } - "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.setOnClickListener { - onAdminMessageDismissClickListener(item) - } - - root.setCardBackgroundColor(backgroundColor?.let { ColorStateList.valueOf(it) }) - item.destinationUrl?.let { url -> - root.setOnClickListener { onAdminMessageClickListener(url) } - } - } - } - class AccountViewHolder(val binding: ItemDashboardAccountBinding) : RecyclerView.ViewHolder(binding.root) @@ -781,9 +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/DashboardFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt index 12a28ea73..1bd258b2d 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,7 +10,6 @@ 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 dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.databinding.FragmentDashboardBinding @@ -25,12 +24,10 @@ 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.capitalise import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.openInternetBrowser import io.github.wulkanowy.utils.toFormattedString import java.time.LocalDate import javax.inject.Inject @@ -73,7 +70,10 @@ 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 { @@ -87,9 +87,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 = { @@ -99,14 +97,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) { @@ -131,7 +121,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 } } @@ -194,14 +183,6 @@ 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() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt index 92665857b..cf99f0c9a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.dashboard -import io.github.wulkanowy.data.db.entities.AdminMessage import io.github.wulkanowy.data.db.entities.Conference import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Grade @@ -17,15 +16,6 @@ sealed class DashboardItem(val type: Type) { abstract val isDataLoaded: Boolean - data class AdminMessages( - val adminMessage: AdminMessage? = null, - override val error: Throwable? = null, - override val isLoading: Boolean = false - ) : DashboardItem(Type.ADMIN_MESSAGE) { - - override val isDataLoaded get() = adminMessage != null - } - data class Account( val student: Student? = null, override val error: Throwable? = null, @@ -106,7 +96,6 @@ sealed class DashboardItem(val type: Type) { } enum class Type { - ADMIN_MESSAGE, ACCOUNT, HORIZONTAL_GROUP, LESSONS, @@ -119,7 +108,6 @@ sealed class DashboardItem(val type: Type) { } enum class Tile { - ADMIN_MESSAGE, ACCOUNT, LUCKY_NUMBER, MESSAGES, @@ -135,7 +123,6 @@ sealed class DashboardItem(val type: Type) { } 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 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 b9625570f..cf4097a4a 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 @@ -21,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 @@ -32,7 +32,7 @@ class DashboardItemMoveCallback( recyclerView: RecyclerView, current: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder - ) = !target.isAdminMessageOrAccountItem + ) = target.bindingAdapterPosition != 0 override fun onMove( recyclerView: RecyclerView, @@ -52,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 d081e19aa..027bcc053 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 @@ -2,11 +2,9 @@ package io.github.wulkanowy.ui.modules.dashboard import io.github.wulkanowy.data.Resource import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.db.entities.AdminMessage import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.enums.MessageFolder -import io.github.wulkanowy.data.repositories.AdminMessageRepository import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository import io.github.wulkanowy.data.repositories.ConferenceRepository import io.github.wulkanowy.data.repositories.ExamRepository @@ -52,8 +50,7 @@ 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 schoolAnnouncementRepository: SchoolAnnouncementRepository ) : BasePresenter(errorHandler, studentRepository) { private val dashboardItemLoadedList = mutableListOf() @@ -82,12 +79,6 @@ class DashboardPresenter @Inject constructor( .launch("dashboard_pref") } - fun onAdminMessageDismissed(adminMessage: AdminMessage) { - preferencesRepository.dismissedAdminMessageIds += adminMessage.id - - loadData(preferencesRepository.selectedDashboardTiles) - } - fun onDragAndDropEnd(list: List) { with(dashboardItemLoadedList) { clear() @@ -124,7 +115,6 @@ class DashboardPresenter @Inject constructor( forceRefresh: Boolean ) = dashboardTilesToLoad.filter { newItemToLoad -> dashboardLoadedTiles.none { it == newItemToLoad } || forceRefresh - || newItemToLoad == DashboardItem.Tile.ADMIN_MESSAGE } private fun removeUnselectedTiles(tilesToLoad: List) { @@ -159,7 +149,7 @@ class DashboardPresenter @Inject constructor( tileList: List, forceRefresh: Boolean ) { - presenterScope.launch { + launch { Timber.i("Loading dashboard account data started") val student = runCatching { studentRepository.getCurrentStudent(true) } .onFailure { @@ -189,7 +179,6 @@ class DashboardPresenter @Inject constructor( loadConferences(student, forceRefresh) } DashboardItem.Type.ADS -> TODO() - DashboardItem.Type.ADMIN_MESSAGE -> loadAdminMessage(student, forceRefresh) } } } @@ -220,11 +209,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 @@ -236,10 +220,6 @@ class DashboardPresenter @Inject constructor( }.toSet() } - fun onAdminMessageSelected(url: String?) { - url?.let { view?.openInternetBrowser(it) } - } - private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) { flow { val semester = semesterRepository.getCurrentSemester(student) @@ -324,17 +304,18 @@ class DashboardPresenter @Inject constructor( gradeRepository.getGrades(student, semester, forceRefresh) }.map { originalResource -> - val filteredSubjectWithGrades = originalResource.data?.first - .orEmpty() - .filter { it.date >= LocalDate.now().minusDays(7) } - .groupBy { it.subject } + 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) - .sortedByDescending { it.date } + .sortedBy { grade -> grade.date } } .toList() - .sortedByDescending { (_, grades) -> grades[0].date } + .sortedBy { subjectWithGrades -> subjectWithGrades.second[0].date } .toMap() Resource( @@ -438,9 +419,9 @@ class DashboardPresenter @Inject constructor( }.map { homeworkResource -> val currentDate = LocalDate.now() - val filteredHomework = homeworkResource.data - ?.filter { (it.date.isAfter(currentDate) || it.date == currentDate) && !it.isDone } - ?.sortedBy { it.date } + val filteredHomework = homeworkResource.data?.filter { + (it.date.isAfter(currentDate) || it.date == currentDate) && !it.isDone + } homeworkResource.copy(data = filteredHomework) }.onEach { @@ -511,37 +492,31 @@ class DashboardPresenter @Inject constructor( end = LocalDate.now().plusDays(7), forceRefresh = forceRefresh ) - } - .map { examResource -> - val sortedExams = examResource.data?.sortedBy { it.date } + }.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 + ) - examResource.copy(data = sortedExams) - } - .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 - ) - - if (!it.data.isNullOrEmpty()) { - firstLoadedItemList += DashboardItem.Type.EXAMS - } - } - 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) + if (!it.data.isNullOrEmpty()) { + firstLoadedItemList += DashboardItem.Type.EXAMS } } - }.launch("dashboard_exams") + 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) + } + } + }.launch("dashboard_exams") } private fun loadConferences(student: Student, forceRefresh: Boolean) { @@ -581,42 +556,6 @@ class DashboardPresenter @Inject constructor( }.launch("dashboard_conferences") } - private fun loadAdminMessage(student: Student, forceRefresh: Boolean) { - flowWithResourceIn { adminMessageRepository.getAdminMessages(student, forceRefresh) } - .map { - val isDismissed = it.data?.id in preferencesRepository.dismissedAdminMessageIds - it.copy(data = it.data.takeUnless { isDismissed }) - } - .onEach { - when (it.status) { - Status.LOADING -> { - Timber.i("Loading dashboard admin message data started") - if (forceRefresh) return@onEach - updateData(DashboardItem.AdminMessages(), forceRefresh) - } - Status.SUCCESS -> { - Timber.i("Loading dashboard admin message result: Success") - updateData( - dashboardItem = DashboardItem.AdminMessages(adminMessage = it.data), - forceRefresh = forceRefresh - ) - } - Status.ERROR -> { - Timber.i("Loading dashboard admin message result: An exception occurred") - errorHandler.dispatch(it.error!!) - updateData( - dashboardItem = DashboardItem.AdminMessages( - adminMessage = it.data, - error = it.error - ), - forceRefresh = forceRefresh - ) - } - } - } - .launch("dashboard_admin_messages") - } - private fun updateData(dashboardItem: DashboardItem, forceRefresh: Boolean) { val isForceRefreshError = forceRefresh && dashboardItem.error != null val isFirstRunDataLoadedError = @@ -629,18 +568,6 @@ class DashboardPresenter @Inject constructor( 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 (forceRefresh) { updateForceRefreshData(dashboardItem) } else { @@ -672,12 +599,9 @@ class DashboardPresenter @Inject constructor( } private fun updateForceRefreshData(dashboardItem: DashboardItem) { - val isNotLoadedAdminMessage = - dashboardItem is DashboardItem.AdminMessages && !dashboardItem.isDataLoaded - with(dashboardItemRefreshLoadedList) { removeAll { it.type == dashboardItem.type } - if (!isNotLoadedAdminMessage) add(dashboardItem) + add(dashboardItem) } val isRefreshItemLoaded = @@ -709,9 +633,7 @@ class DashboardPresenter @Inject constructor( itemsLoadedList: List, forceRefresh: Boolean ) { - val filteredItems = itemsLoadedList.filterNot { - it.type == DashboardItem.Type.ACCOUNT || it.type == DashboardItem.Type.ADMIN_MESSAGE - } + val filteredItems = itemsLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT } val isAccountItemError = itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null val isGeneralError = @@ -743,13 +665,10 @@ class DashboardPresenter @Inject constructor( val dashboardItemsPosition = preferencesRepository.dashboardItemsPosition dashboardItemLoadedList.sortBy { tile -> - val defaultPosition = if (tile is DashboardItem.AdminMessages) { - -1 - } else { + dashboardItemsPosition?.getOrDefault( + tile.type, tile.type.ordinal + 100 - } - - dashboardItemsPosition?.getOrDefault(tile.type, defaultPosition) ?: tile.type.ordinal + ) ?: tile.type.ordinal } } } \ 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 730e19a35..d5c5e5a70 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 @@ -23,8 +23,4 @@ interface DashboardView : BaseView { 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/notification/NotificationDebugPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/NotificationDebugPresenter.kt index d0dfcd696..07468daa6 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 042cf07e7..000000000 --- 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/timetable.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt deleted file mode 100644 index 428c001d6..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.github.wulkanowy.ui.modules.debug.notification.mock - -import io.github.wulkanowy.data.db.entities.Timetable -import java.time.LocalDate -import java.time.LocalDateTime -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 = LocalDateTime.now().plusHours(1), - end = LocalDateTime.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/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt index ddd0e4a19..fb7939bc5 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 @@ -64,7 +64,7 @@ class ExamFragment : BaseFragment(R.layout.fragment_exam), examPreviousButton.setOnClickListener { presenter.onPreviousWeek() } examNextButton.setOnClickListener { presenter.onNextWeek() } - examNavContainer.elevation = requireContext().dpToPx(8f) + examNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } 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 2784f429f..4a3049721 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 @@ -131,9 +131,7 @@ class GradeAverageProvider @Inject constructor( val updatedFirstSemesterGrades = firstSemesterSubject?.grades?.updateModifiers(student).orEmpty() - (updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage( - isOptionalArithmeticAverage - ) + (updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(isOptionalArithmeticAverage) } else { secondSemesterSubject.average } @@ -149,8 +147,7 @@ class GradeAverageProvider @Inject constructor( return if (!isAnyVulcanAverage || isGradeAverageForceCalc) { val secondSemesterAverage = - secondSemesterSubject.grades.updateModifiers(student) - .calcAverage(isOptionalArithmeticAverage) + secondSemesterSubject.grades.updateModifiers(student).calcAverage(isOptionalArithmeticAverage) val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student) ?.calcAverage(isOptionalArithmeticAverage) ?: secondSemesterAverage @@ -216,8 +213,7 @@ class GradeAverageProvider @Inject constructor( proposedPoints = "", finalPoints = "", pointsSum = "", - average = if (calcAverage) details.updateModifiers(student) - .calcAverage(isOptionalArithmeticAverage) else .0 + average = if (calcAverage) details.updateModifiers(student).calcAverage(isOptionalArithmeticAverage) else .0 ) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeExpandMode.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeExpandMode.kt deleted file mode 100644 index 722e986ee..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeExpandMode.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.wulkanowy.ui.modules.grade - -enum class GradeExpandMode(val value: String) { - ONE("one"), UNLIMITED("any"), ALWAYS_EXPANDED("always"); - - companion object { - fun getByValue(value: String) = values().firstOrNull { it.value == value } ?: ONE - } -} \ No newline at end of file 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 0a8561eec..b3ef3037a 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,7 +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.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Semester @@ -30,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 @@ -69,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() } 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 76e88bcdb..504c730de 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 @@ -101,6 +101,7 @@ class GradePresenter @Inject constructor( private fun loadData() { flowWithResource { val student = studentRepository.getCurrentStudent() + delay(200) semesterRepository.getSemesters(student, refreshOnNoCurrent = true) }.onEach { when (it.status) { 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 d96ac0928..01631140c 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 @@ -5,7 +5,6 @@ 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 @@ -14,11 +13,9 @@ import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.databinding.HeaderGradeDetailsBinding import io.github.wulkanowy.databinding.ItemGradeDetailsBinding import io.github.wulkanowy.ui.base.BaseExpandableAdapter -import io.github.wulkanowy.ui.modules.grade.GradeExpandMode import io.github.wulkanowy.utils.getBackgroundColor import io.github.wulkanowy.utils.toFormattedString import timber.log.Timber -import java.util.BitSet import javax.inject.Inject class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter() { @@ -27,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 = { _, _ -> } 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) { @@ -52,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() @@ -68,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() } } @@ -114,91 +106,46 @@ 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 + + if (expandedPosition != NO_POSITION) { + refreshList(headers.toMutableList().apply { + addAll(headerPosition + 1, header.grades) + }) + scrollToHeaderWithSubItems(headerPosition, header.grades.size) + } else { + refreshList(headers) + } } } } - 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) - } - } + 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) { - val context = holder.binding.root.context - with(holder.binding) { gradeItemValue.run { text = grade.entry @@ -207,37 +154,26 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter grade.description grade.gradeSymbol.isNotBlank() -> grade.gradeSymbol - else -> context.getString(R.string.all_no_description) + else -> root.context.getString(R.string.all_no_description) } gradeItemDate.text = grade.date.toFormattedString() - gradeItemWeight.text = "${context.getString(R.string.grade_weight)}: ${grade.weight}" + 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) - } + holder.bindingAdapterPosition.let { if (it != NO_POSITION) onClickListener(grade, it) } } } } - 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 class HeaderViewHolder(val binding: HeaderGradeDetailsBinding) : RecyclerView.ViewHolder(binding.root) private class ItemViewHolder(val binding: ItemGradeDetailsBinding) : RecyclerView.ViewHolder(binding.root) - private class GradeDetailsDiffUtil( - private val old: List, - 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/GradeDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt index c93600d49..9d4da767d 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 @@ -12,7 +12,6 @@ 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.ui.modules.grade.GradeExpandMode import io.github.wulkanowy.databinding.FragmentGradeDetailsBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment @@ -80,10 +79,10 @@ class GradeDetailsFragment : else false } - override fun updateData(data: List, expandMode: GradeExpandMode, gradeColorTheme: String) { + override fun updateData(data: List, isGradeExpandable: Boolean, gradeColorTheme: String) { with(gradeDetailsAdapter) { colorTheme = gradeColorTheme - setDataItems(data, expandMode) + setDataItems(data, isGradeExpandable) notifyDataSetChanged() } } 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 54d4f461e..7544d2aa1 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 @@ -9,7 +9,6 @@ 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.GradeExpandMode 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 @@ -17,7 +16,6 @@ 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.catch import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.onEach import timber.log.Timber @@ -48,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) { @@ -115,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() } } @@ -159,7 +157,7 @@ class GradeDetailsPresenter @Inject constructor( showContent(true) updateData( data = items, - expandMode = preferencesRepository.gradeExpandMode, + isGradeExpandable = preferencesRepository.isGradeExpandable, gradeColorTheme = preferencesRepository.gradeColorTheme ) notifyParentDataLoaded(semesterId) @@ -177,7 +175,7 @@ class GradeDetailsPresenter @Inject constructor( showContent(items.isNotEmpty()) updateData( data = items, - expandMode = preferencesRepository.gradeExpandMode, + isGradeExpandable = preferencesRepository.isGradeExpandable, gradeColorTheme = preferencesRepository.gradeColorTheme ) } @@ -199,9 +197,6 @@ class GradeDetailsPresenter @Inject constructor( enableSwipe(true) notifyParentDataLoaded(semesterId) } - }.catch { - errorHandler.dispatch(it) - view?.notifyParentDataLoaded(semesterId) }.launch() } @@ -218,7 +213,6 @@ class GradeDetailsPresenter @Inject constructor( setErrorDetails(message) showErrorView(true) showEmpty(false) - showProgress(false) } else showError(message, error) } } @@ -241,24 +235,14 @@ 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() } 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 556332290..e71fcc3c8 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,7 +1,6 @@ package io.github.wulkanowy.ui.modules.grade.details import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.ui.modules.grade.GradeExpandMode import io.github.wulkanowy.ui.base.BaseView interface GradeDetailsView : BaseView { @@ -10,7 +9,7 @@ interface GradeDetailsView : BaseView { fun initView() - fun updateData(data: List, expandMode: GradeExpandMode, gradeColorTheme: String) + fun updateData(data: List, isGradeExpandable: Boolean, gradeColorTheme: String) fun updateItem(item: Grade, position: Int) 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 16162d119..0adac300a 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 @@ -43,7 +43,7 @@ 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( this, savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType @@ -68,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)) 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 082c847e5..0754361c9 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,7 +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.utils.calcFinalAverage +import io.github.wulkanowy.utils.calcAverage import java.util.Locale import javax.inject.Inject @@ -25,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) { @@ -64,7 +60,7 @@ class GradeSummaryAdapter @Inject constructor( 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 ) @@ -87,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 3810902ff..0ac16fb36 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,7 +5,6 @@ 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 dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -49,11 +48,6 @@ class GradeSummaryFragment : } override fun initView() { - with(gradeSummaryAdapter) { - onCalculatedHelpClickListener = presenter::onCalculatedAverageHelpClick - onFinalHelpClickListener = presenter::onFinalAverageHelpClick - } - with(binding.gradeSummaryRecycler) { layoutManager = LinearLayoutManager(context) adapter = gradeSummaryAdapter @@ -61,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() } } @@ -117,22 +107,6 @@ class GradeSummaryFragment : binding.gradeSummarySwipe.isRefreshing = show } - override fun showCalculatedAverageHelpDialog() { - AlertDialog.Builder(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() { - AlertDialog.Builder(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 933633dca..7adfd7e58 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 @@ -135,14 +135,6 @@ 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) } 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 156731c31..974d91415 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 d4eaade2c..1d9434dc7 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 @@ -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(8f) + homeworkNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -125,14 +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 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 d7d5d7cb9..11c54dc24 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 @@ -78,11 +78,7 @@ 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() { 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 7c05ab865..a1d6a04a9 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,7 +33,5 @@ interface HomeworkView : BaseView { fun showNextButton(show: Boolean) - fun showHomeworkDialog(homework: Homework) - - fun showAddHomeworkDialog() + 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 12168f142..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt +++ /dev/null @@ -1,124 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework.add - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.widget.doOnTextChanged -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.databinding.DialogHomeworkAddBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.toFormattedString -import io.github.wulkanowy.utils.toLocalDateTime -import io.github.wulkanowy.utils.toTimestamp -import java.time.LocalDate -import javax.inject.Inject - -@AndroidEntryPoint -class HomeworkAddDialog : BaseDialogFragment(), HomeworkAddView { - - @Inject - lateinit var presenter: HomeworkAddPresenter - - private var date: LocalDate? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = DialogHomeworkAddBinding.inflate(inflater).apply { binding = this }.root - - 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(currentDate: LocalDate) { - val constraintsBuilder = CalendarConstraints.Builder().apply { - setStart(LocalDate.now().toEpochDay()) - } - val datePicker = - MaterialDatePicker.Builder.datePicker() - .setCalendarConstraints(constraintsBuilder.build()) - .setSelection(currentDate.toTimestamp()) - .build() - - datePicker.addOnPositiveButtonClickListener { - date = it.toLocalDateTime().toLocalDate() - binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString()) - } - - if (!parentFragmentManager.isStateSaved) { - datePicker.show(this.parentFragmentManager, null) - } - } - - 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 3639c2fef..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddPresenter.kt +++ /dev/null @@ -1,92 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework.add - -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.flowWithResource -import io.github.wulkanowy.utils.toLocalDate -import kotlinx.coroutines.flow.onEach -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) { - flowWithResource { - 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 } - ) - }.onEach { - when (it.status) { - Status.LOADING -> Timber.i("Homework insert start") - Status.SUCCESS -> { - Timber.i("Homework insert: Success") - view?.run { - showSuccessMessage() - closeDialog() - } - } - Status.ERROR -> { - Timber.i("Homework insert result: An exception occurred") - errorHandler.dispatch(it.error!!) - } - } - }.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 3bb304d9c..000000000 --- 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(currentDate: 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 e03707a5c..cd9a7e851 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 @@ -39,8 +37,6 @@ class HomeworkDetailsAdapter @Inject constructor() : var onFullScreenExitClickListener = {} - var onDeleteClickListener: (homework: Homework) -> Unit = {} - override fun getItemCount() = 1 + if (attachments.isNotEmpty()) attachments.size + 1 else 0 override fun getItemViewType(position: Int) = when (position) { @@ -53,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)) } } @@ -73,15 +63,12 @@ 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 + 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 { @@ -94,9 +81,6 @@ class HomeworkDetailsAdapter @Inject constructor() : homeworkDialogFullScreenExit.visibility = GONE onFullScreenExitClickListener() } - homeworkDialogDelete.setOnClickListener { - onDeleteClickListener(homework!!) - } } } 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 f9d463510..93045a481 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 @@ -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 { @@ -85,17 +82,12 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) presenter.isHomeworkFullscreen = false } - onDeleteClickListener = { homework -> presenter.deleteHomework(homework) } 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 ea9b47a05..ca6fc71ee 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 @@ -33,25 +33,6 @@ class HomeworkDetailsPresenter @Inject constructor( Timber.i("Homework details view was initialized") } - fun deleteHomework(homework: Homework) { - flowWithResource { homeworkRepository.deleteHomework(homework) }.onEach { - when (it.status) { - Status.LOADING -> Timber.i("Homework delete start") - Status.SUCCESS -> { - Timber.i("Homework delete: Success") - view?.run { - showMessage(homeworkDeleteSuccess) - closeDialog() - } - } - Status.ERROR -> { - Timber.i("Homework delete result: An exception occurred") - errorHandler.dispatch(it.error!!) - } - } - }.launch("delete") - } - fun toggleDone(homework: Homework) { flowWithResource { homeworkRepository.toggleDone(homework) }.onEach { when (it.status) { 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 4a47de43b..697f22335 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 e607cef15..8d96a498f 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 @@ -24,24 +24,18 @@ class LoginActivity : BaseActivity(), Logi @Inject override lateinit var presenter: LoginPresenter - private val pagerAdapter by lazy { - BaseFragmentPagerAdapter( - fragmentManager = supportFragmentManager, - pagesCount = 5, - lifecycle = lifecycle, - ) - } + private val loginAdapter = BaseFragmentPagerAdapter(supportFragmentManager) @Inject lateinit var updateHelper: UpdateHelper - override val currentViewIndex get() = binding.loginViewpager.currentItem - 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) @@ -71,25 +65,23 @@ class LoginActivity : BaseActivity(), Logi setDisplayShowTitleEnabled(false) } - with(binding.loginViewpager) { - adapter = pagerAdapter - isUserInputEnabled = false - offscreenPageLimit = 2 - setOnSelectPageListener(presenter::onViewSelected) + with(loginAdapter) { + containerId = binding.loginViewpager.id + addFragments( + listOf( + LoginFormFragment.newInstance(), + LoginSymbolFragment.newInstance(), + LoginStudentSelectFragment.newInstance(), + LoginAdvancedFragment.newInstance(), + LoginRecoverFragment.newInstance() + ) + ) } - with(pagerAdapter) { - containerId = binding.loginViewpager.id - itemFactory = { - when (it) { - 0 -> LoginFormFragment.newInstance() - 1 -> LoginSymbolFragment.newInstance() - 2 -> LoginStudentSelectFragment.newInstance() - 3 -> LoginAdvancedFragment.newInstance() - 4 -> LoginRecoverFragment.newInstance() - else -> throw IllegalStateException() - } - } + with(binding.loginViewpager) { + offscreenPageLimit = 2 + adapter = loginAdapter + setOnSelectPageListener(presenter::onViewSelected) } } @@ -111,12 +103,13 @@ class LoginActivity : BaseActivity(), Logi } override fun notifyInitSymbolFragment(loginData: Triple) { - (pagerAdapter.getFragmentInstance(1) as? LoginSymbolFragment) - ?.onParentInitSymbolFragment(loginData) + (loginAdapter.getFragmentInstance(1) as? LoginSymbolFragment)?.onParentInitSymbolFragment( + loginData + ) } override fun notifyInitStudentSelectFragment(studentsWithSemesters: List) { - (pagerAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment) + (loginAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment) ?.onParentInitStudentSelectFragment(studentsWithSemesters) } 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 ea7215cea..ed4563246 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,8 +1,7 @@ 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.mobile.exception.InvalidPinException import io.github.wulkanowy.sdk.mobile.exception.InvalidSymbolException @@ -12,10 +11,9 @@ import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject -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 = {} @@ -26,9 +24,8 @@ class LoginErrorHandler @Inject constructor(@ApplicationContext context: Context 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 InvalidTokenException -> onInvalidToken(resources.getString(R.string.login_invalid_token)) 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 bc29cd146..9231914c8 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 @@ -51,12 +51,10 @@ class LoginAdvancedFragment : private lateinit var hostSymbols: Array override val formHostValue: String - get() = hostValues.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) - .orEmpty() + 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() @@ -94,62 +92,39 @@ class LoginAdvancedFragment : loginFormSignIn.setOnClickListener { presenter.onSignInClick() } loginTypeSwitch.setOnCheckedChangeListener { _, checkedId -> - presenter.onLoginModeSelected( - when (checkedId) { - R.id.loginTypeApi -> Sdk.Mode.API - 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) @@ -170,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) } } @@ -191,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) } } @@ -202,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) } } @@ -226,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) } } @@ -240,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) } } @@ -321,13 +296,11 @@ class LoginAdvancedFragment : } 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] - ) - ) + (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 17d8c5ecb..891a6b0bb 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 @@ -34,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") } 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 1d2b2856d..029a6b4d4 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 @@ -49,7 +49,7 @@ interface LoginAdvancedView : BaseView { fun setErrorPassInvalid(focus: Boolean) - fun setErrorPassIncorrect(message: String?) + fun setErrorPassIncorrect() fun clearUsernameError() 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 c741da429..e383072ec 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,7 +5,6 @@ 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 @@ -42,12 +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() + 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) @@ -91,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() } } } @@ -123,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) } } @@ -141,7 +132,7 @@ class LoginFormFragment : BaseFragment(R.layout.fragme override fun setErrorPassRequired(focus: Boolean) { with(binding.loginFormPassLayout) { - error = getString(R.string.error_field_required) + error = getString(R.string.login_field_required) } } @@ -151,35 +142,24 @@ class LoginFormFragment : BaseFragment(R.layout.fragme } } - override fun setErrorPassIncorrect(message: String?) { - with(binding) { - loginFormUsernameLayout.error = " " - loginFormPassLayout.error = " " - 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.loginFormErrorBox.isVisible = false - } - - override fun clearHostError() { - binding.loginFormHostLayout.error = null - binding.loginFormErrorBox.isVisible = false } override fun showSoftKeyboard() { @@ -203,18 +183,12 @@ class LoginFormFragment : BaseFragment(R.layout.fragme binding.loginFormVersion.text = "v${appInfo.versionName}" } - override fun notifyParentAccountLogged( - studentsWithSemesters: List, - loginData: Triple - ) { + 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 showContact(show: Boolean) { @@ -236,10 +210,7 @@ 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() { @@ -252,8 +223,7 @@ 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, 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 21cdf2a04..99dcf1bb3 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 @@ -30,7 +30,7 @@ class LoginFormPresenter @Inject constructor( showVersion() loginErrorHandler.onBadCredentials = { - setErrorPassIncorrect(it.takeIf { !it.isNullOrBlank() }) + setErrorPassIncorrect() showSoftKeyboard() Timber.i("Entered wrong username or password") } @@ -49,7 +49,6 @@ class LoginFormPresenter @Inject constructor( view?.apply { clearPassError() clearUsernameError() - clearHostError() if (formHostValue.contains("fakelog")) { setCredentials("jan@fakelog.cf", "jan123") } @@ -76,10 +75,7 @@ class LoginFormPresenter @Inject constructor( val usernameHost = username.substringAfter("@") hosts[usernameHost]?.let { - view?.run { - setHost(it) - clearHostError() - } + view?.setHost(it) } } } @@ -94,10 +90,10 @@ class LoginFormPresenter @Inject constructor( flowWithResource { studentRepository.getStudentsScrapper( - email = email, - password = password, - scrapperBaseUrl = host, - symbol = symbol + email, + password, + host, + symbol ) }.onEach { when (it.status) { 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 300573559..079629ef6 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 @@ -37,7 +37,7 @@ interface LoginFormView : BaseView { fun setErrorPassInvalid(focus: Boolean) - fun setErrorPassIncorrect(message: String?) + fun setErrorPassIncorrect() fun setErrorEmailInvalid(domain: String) @@ -45,8 +45,6 @@ interface LoginFormView : BaseView { fun clearPassError() - fun clearHostError() - fun showSoftKeyboard() fun hideSoftKeyboard() 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 a91dfb618..2e2f9f5ce 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 @@ -99,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) } } 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 ac4c03130..8619369dd 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,15 +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 = {} @@ -17,8 +15,7 @@ class RecoverErrorHandler @Inject constructor(@ApplicationContext context: Conte 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/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt index 87cb505c4..e71fc0f6f 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 @@ -66,8 +66,7 @@ class LoginStudentSelectFragment : } override fun openMainView() { - startActivity(MainActivity.getStartIntent(requireContext())) - requireActivity().finish() + activity?.let { startActivity(MainActivity.getStartIntent(context = it, clear = true)) } } override fun showProgress(show: Boolean) { @@ -109,8 +108,7 @@ 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.systemModel, + body = requireContext().getString(R.string.login_email_text, appInfo.systemModel, appInfo.systemVersion.toString(), appInfo.versionName, "Select users to log in", 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 f0f5586cc..c344bf441 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 @@ -78,9 +78,7 @@ class LoginStudentSelectPresenter @Inject constructor( 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) - } + studentWithSemesters to it.data!!.any { item -> compareStudents(studentWithSemesters.student, item.student) } }) Status.ERROR -> { errorHandler.dispatch(it.error!!) @@ -97,32 +95,35 @@ class LoginStudentSelectPresenter @Inject constructor( } private fun registerStudents(studentsWithSemesters: List) { - flowWithResource { studentRepository.saveStudents(studentsWithSemesters) } - .onEach { - when (it.status) { - Status.LOADING -> view?.run { - Timber.i("Registration started") - showProgress(true) - showContent(false) - } - 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) - } + 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") } fun onDiscordClick() { @@ -133,10 +134,7 @@ class LoginStudentSelectPresenter @Inject constructor( 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", 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 a8086935b..e2c37db61 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,7 +7,6 @@ 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.text.parseAsHtml import androidx.core.widget.doOnTextChanged import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -59,13 +58,7 @@ 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))) } } } @@ -74,10 +67,6 @@ class LoginSymbolFragment : presenter.onParentInitSymbolView(loginData) } - override fun setLoginToHeading(login: String) { - binding.loginSymbolHeader.text = getString(R.string.login_header_symbol, login).parseAsHtml() - } - override fun setErrorSymbolIncorrect() { binding.loginSymbolNameLayout.apply { requestFocus() @@ -88,7 +77,7 @@ class LoginSymbolFragment : override fun setErrorSymbolRequire() { binding.loginSymbolNameLayout.apply { requestFocus() - error = getString(R.string.error_field_required) + error = getString(R.string.login_field_required) } } @@ -138,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) { @@ -149,8 +135,7 @@ 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, 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 1ba7e5b32..4593d880a 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 @@ -32,16 +32,6 @@ class LoginSymbolPresenter @Inject constructor( } if (savedLoginData is Triple<*, *, *>) { loginData = savedLoginData as Triple - view.setLoginToHeading(requireNotNull(loginData?.first)) - } - } - - fun onParentInitSymbolView(loginData: Triple) { - this.loginData = loginData - view?.apply { - setLoginToHeading(loginData.first) - clearAndFocusSymbol() - showSoftKeyboard() } } @@ -50,24 +40,14 @@ class LoginSymbolPresenter @Inject constructor( } fun attemptLogin(symbol: String) { - if (loginData == null) { - Timber.w("LoginSymbolPresenter - Login data is null") - return - } + if (loginData == null) throw IllegalArgumentException("Login data is null") if (symbol.isBlank()) { view?.setErrorSymbolRequire() return } - flowWithResource { - studentRepository.getStudentsScrapper( - email = loginData!!.first, - password = loginData!!.second, - scrapperBaseUrl = loginData!!.third, - symbol = symbol, - ) - }.onEach { + flowWithResource { studentRepository.getStudentsScrapper(loginData!!.first, loginData!!.second, loginData!!.third, symbol) }.onEach { when (it.status) { Status.LOADING -> view?.run { Timber.i("Login with symbol started") @@ -118,6 +98,14 @@ class LoginSymbolPresenter @Inject constructor( }.launch("login") } + fun onParentInitSymbolView(loginData: Triple) { + this.loginData = loginData + view?.apply { + clearAndFocusSymbol() + showSoftKeyboard() + } + } + fun onFaqClick() { view?.openFaqPage() } 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 75523a7c7..830c77d17 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 @@ -9,8 +9,6 @@ interface LoginSymbolView : BaseView { fun initView() - fun setLoginToHeading(login: String) - fun setErrorSymbolIncorrect() fun setErrorSymbolRequire() 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 49d094b78..dc141f8d3 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 @@ -65,7 +65,7 @@ class LuckyNumberHistoryFragment : luckyNumberHistoryPreviousButton.setOnClickListener { presenter.onPreviousWeek() } luckyNumberHistoryNextButton.setOnClickListener { presenter.onNextWeek() } - luckyNumberHistoryNavContainer.elevation = requireContext().dpToPx(8f) + luckyNumberHistoryNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -131,9 +131,7 @@ class LuckyNumberHistoryFragment : presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth) } - if (!parentFragmentManager.isStateSaved) { - datePicker.show(parentFragmentManager, null) - } + datePicker.show(this@LuckyNumberHistoryFragment.parentFragmentManager, null) } override fun showContent(show: Boolean) { 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 2b2d18fa0..49a199431 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,6 +1,7 @@ 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 @@ -17,9 +18,8 @@ import io.github.wulkanowy.data.db.SharedPrefProvider 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.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 @@ -39,8 +39,6 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { companion object { - const val LUCKY_NUMBER_PENDING_INTENT_ID = 200 - fun getStudentWidgetKey(appWidgetId: Int) = "lucky_number_widget_student_$appWidgetId" fun getThemeWidgetKey(appWidgetId: Int) = "lucky_number_widget_theme_$appWidgetId" @@ -50,31 +48,18 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { 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 luckyNumber = - getLuckyNumber(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), 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 remoteView = - RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context)) - .apply { - setTextViewText( - R.id.luckyNumberWidgetNumber, - luckyNumber?.luckyNumber?.toString() ?: "#" - ) - setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent) - } + 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 remoteView = RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context)).apply { + setTextViewText(R.id.luckyNumberWidgetNumber, luckyNumber?.luckyNumber?.toString() ?: "#") + setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent) + } setStyles(remoteView, appWidgetId) appWidgetManager.updateAppWidget(appWidgetId, remoteView) @@ -93,12 +78,7 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { } } - override fun onAppWidgetOptionsChanged( - context: Context, - appWidgetManager: AppWidgetManager, - appWidgetId: Int, - newOptions: Bundle? - ) { + 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)) @@ -108,12 +88,8 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { } 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() + 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()) @@ -136,11 +112,7 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { } } - private fun RemoteViews.setVisibility( - imageTop: Boolean, - imageLeft: Boolean, - title: Boolean = false - ) { + 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) @@ -180,8 +152,7 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { 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 isSystemDarkMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES return if (savedTheme == 1L || (savedTheme == 2L && isSystemDarkMode)) { R.layout.widget_luckynumber_dark 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 d81abe345..d758ac0da 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,11 +1,20 @@ 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 androidx.annotation.RequiresApi +import androidx.core.content.getSystemService import androidx.core.view.ViewCompat import androidx.core.view.isVisible import androidx.fragment.app.DialogFragment @@ -21,8 +30,19 @@ 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.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.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 @@ -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" + 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 { - putExtra(EXTRA_START_DESTINATION, destination) + if (clear) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK + startMenu?.let { putExtra(EXTRA_START_MENU, it.id) } } } @@ -85,20 +106,42 @@ class MainActivity : BaseActivity(), MainVie 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) - this.savedInstanceState = savedInstanceState messageContainer = binding.mainMessageContainer updateHelper.messageContainer = binding.mainFragmentContainer - val destination = (intent.getSerializableExtra(EXTRA_START_DESTINATION) as Destination?) - ?.takeIf { savedInstanceState == null } + 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]) + } + + if (appInfo.systemVersion >= Build.VERSION_CODES.N_MR1) { + initShortcuts() + } - presenter.onAttachView(this, destination) updateHelper.checkAndInstallUpdates(this) } @@ -114,47 +157,71 @@ class MainActivity : BaseActivity(), MainVie 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, rootDestinations: List) { - initializeToolbar() - initializeBottomNavigation(startMenuIndex) - initializeNavController(startMenuIndex, rootDestinations) - } - - private fun initializeNavController(startMenuIndex: Int, rootDestinations: List) { - with(navController) { - setOnViewChangeListener { destinationView -> - presenter.onViewChange(destinationView) - analytics.setCurrentScreen( - this@MainActivity, - destinationView::class.java.simpleName - ) - } - fragmentHideStrategy = HIDE - rootFragments = rootDestinations.map { it.fragment } - - 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) { with(binding.mainBottomNav) { with(menu) { add(Menu.NONE, 0, Menu.NONE, R.string.dashboard_title) @@ -172,6 +239,36 @@ class MainActivity : BaseActivity(), MainVie setOnItemSelectedListener { presenter.onTabSelected(it.itemId, false) } setOnItemReselectedListener { presenter.onTabSelected(it.itemId, true) } } + + with(navController) { + setOnViewChangeListener { section, name -> + if (section == MainView.Section.ACCOUNT || section == MainView.Section.STUDENT_INFO) { + binding.mainBottomNav.isVisible = false + + 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) + } + fragmentHideStrategy = HIDE + rootFragments = listOf( + DashboardFragment.newInstance(), + GradeFragment.newInstance(), + AttendanceFragment.newInstance(), + TimetableFragment.newInstance(), + MoreFragment.newInstance() + ) + } } override fun onPreferenceStartFragment( @@ -220,22 +317,6 @@ class MainActivity : BaseActivity(), MainVie ViewCompat.setElevation(binding.mainToolbar, if (show) dpToPx(4f) else 0f) } - override fun showBottomNavigation(show: Boolean) { - binding.mainBottomNav.isVisible = show - - if (appInfo.systemVersion >= P) { - window.navigationBarColor = if (show) { - getThemeAttrColor(android.R.attr.navigationBarColor) - } else { - getThemeAttrColor(R.attr.colorSurface) - } - } - } - - override fun openMoreDestination(destination: Destination) { - pushView(destination.fragment) - } - override fun notifyMenuViewReselected() { (navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected() } @@ -292,5 +373,6 @@ class MainActivity : BaseActivity(), MainVie 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 c78931531..4805b5a1c 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 @@ -6,15 +6,10 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository 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.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.grade.GradeView -import io.github.wulkanowy.ui.modules.message.MessageView -import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersView -import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView +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.flowWithResource import kotlinx.coroutines.flow.onEach @@ -32,40 +27,19 @@ class MainPresenter @Inject constructor( private var studentsWitSemesters: List? = null - private val rootDestinationTypeList = listOf( - Destination.Type.DASHBOARD, - Destination.Type.GRADE, - Destination.Type.ATTENDANCE, - Destination.Type.TIMETABLE, - Destination.Type.MORE - ) - - private val Destination?.startMenuIndex - get() = when { - this == null -> prefRepository.startMenuIndex - type in rootDestinationTypeList -> { - rootDestinationTypeList.indexOf(type) - } - else -> 4 - } - - fun onAttachView(view: MainView, initDestination: Destination?) { + fun onAttachView(view: MainView, initMenu: MainView.Section?) { super.onAttachView(view) - - val startMenuIndex = initDestination.startMenuIndex - val destinations = rootDestinationTypeList.map { - if (it == initDestination?.type) initDestination else it.defaultDestination - } - - view.initView(startMenuIndex, 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() - - 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() { @@ -90,10 +64,9 @@ class MainPresenter @Inject constructor( }.launch("avatar") } - fun onViewChange(destinationView: BaseView) { + fun onViewChange(section: MainView.Section?) { view?.apply { - showBottomNavigation(shouldShowBottomNavigation(destinationView)) - showActionBarElevation(shouldShowActionBarElevation(destinationView)) + showActionBarElevation(section != GRADE && section != MESSAGE && section != SCHOOL) currentViewTitle?.let { setViewTitle(it) } currentViewSubtitle?.let { setViewSubTitle(it.ifBlank { null }) } currentStackSize?.let { @@ -103,20 +76,6 @@ class MainPresenter @Inject constructor( } } - private fun shouldShowActionBarElevation(destination: BaseView) = when (destination) { - is GradeView, - is MessageView, - is SchoolAndTeachersView -> false - else -> true - } - - private fun shouldShowBottomNavigation(destination: BaseView) = when (destination) { - is AccountView, - is StudentInfoView, - is AccountDetailsView -> false - else -> true - } - fun onAccountManagerSelected(): Boolean { if (studentsWitSemesters.isNullOrEmpty()) return true @@ -175,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 3a57fcc6b..8851f5878 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,10 +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 interface MainView : BaseView { + var startMenuIndex: Int + + var startMenuMoreIndex: Int + val isRootView: Boolean val currentViewTitle: String? @@ -15,7 +18,7 @@ interface MainView : BaseView { val currentStackSize: Int? - fun initView(startMenuIndex: Int, rootDestinations: List) + fun initView() fun switchMenuView(position: Int) @@ -25,8 +28,6 @@ interface MainView : BaseView { fun showActionBarElevation(show: Boolean) - fun showBottomNavigation(show: Boolean) - fun notifyMenuViewReselected() fun notifyMenuViewChanged() @@ -41,8 +42,6 @@ interface MainView : BaseView { fun showInAppReview() - fun openMoreDestination(destination: Destination) - interface MainChildView { fun onFragmentReselected() @@ -58,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 acf3133d9..72fc627f8 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,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.data.enums.MessageFolder.RECEIVED @@ -27,13 +26,7 @@ class MessageFragment : BaseFragment(R.layout.fragment_m @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() @@ -50,35 +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() } } @@ -102,8 +86,7 @@ class MessageFragment : BaseFragment(R.layout.fragment_m } override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) { - (pagerAdapter.getFragmentInstance(index) as? MessageTabFragment) - ?.onParentLoadData(forceRefresh) + (pagerAdapter.getFragmentInstance(index) as? MessageTabFragment)?.onParentLoadData(forceRefresh) } override fun openSendMessage() { 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 9e19517bd..7b8c3d0f5 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 @@ -15,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() 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 d75128be1..421453c94 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 @@ -79,7 +79,12 @@ class MessagePreviewAdapter @Inject constructor() : 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 -> { context.getString(R.string.message_read, context.getString(R.string.all_yes)) 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 e1cc2e374..74f8f57ec 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,6 +13,7 @@ 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.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint @@ -23,6 +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.AppInfo import io.github.wulkanowy.utils.shareText import javax.inject.Inject @@ -37,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 @@ -134,7 +140,7 @@ class MessagePreviewFragment : menuForwardButton?.isVisible = show menuDeleteButton?.isVisible = show menuShareButton?.isVisible = show - menuPrintButton?.isVisible = show + menuPrintButton?.isVisible = show && appInfo.systemVersion >= Build.VERSION_CODES.LOLLIPOP } override fun setDeletedOptionsLabels() { @@ -169,6 +175,7 @@ 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() { @@ -183,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 eb33ee6ea..702e54676 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,6 +1,7 @@ package io.github.wulkanowy.ui.modules.message.preview import android.annotation.SuppressLint +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 @@ -10,6 +11,7 @@ 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 @@ -22,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 @@ -109,11 +112,10 @@ class MessagePreviewPresenter @Inject constructor( fun onShare(): Boolean { 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}" + 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}" attachments?.let { attachments -> if (attachments.isNotEmpty()) { @@ -125,10 +127,7 @@ class MessagePreviewPresenter @Inject constructor( } } - view?.shareText( - text, - "FW: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}" - ) + view?.shareText(text, "FW: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}") return true } return false @@ -136,6 +135,7 @@ class MessagePreviewPresenter @Inject constructor( @SuppressLint("NewApi") fun onPrint(): Boolean { + 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 { @@ -154,9 +154,7 @@ class MessagePreviewPresenter @Inject constructor( view?.apply { val html = printHTML - .replace( - "%SUBJECT%", - it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }) + .replace("%SUBJECT%", it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }) .replace("%CONTENT%", messageContent) .replace("%INFO%", infoContent) printDocument(html, jobName) 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 88fe77d94..583ba6878 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 @@ -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 bd14bc893..26ab7f488 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 70f9a9b54..1432a9945 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 @@ -118,7 +118,7 @@ class SendMessageActivity : BaseActivity - MessageTabDataItem.MessageItem(message) + val newItems = listOf(MessageTabDataItem.Header) + filteredData.map { + MessageTabDataItem.MessageItem(it) } - val messageItemsWithHeader = - listOf(MessageTabDataItem.Header) + messageItems - - updateData(messageItemsWithHeader, folder.id == MessageFolder.SENT.id) + updateData(newItems, folder.id == MessageFolder.SENT.id) notifyParentDataLoaded() } } @@ -161,9 +157,6 @@ class MessageTabPresenter @Inject constructor( enableSwipe(true) notifyParentDataLoaded() } - }.catch { - errorHandler.dispatch(it) - view?.notifyParentDataLoaded() }.launch() } @@ -174,20 +167,19 @@ 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 -> 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 53049891a..9591867df 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 @@ -11,6 +11,7 @@ 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 @@ -147,6 +148,6 @@ class MobileDevicePresenter @Inject constructor( errorHandler.dispatch(it.error!!) } } - }.launch("unregister") + }.launchIn(this) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt index 145b12a35..2f0957c46 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt @@ -11,7 +11,6 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.conference.ConferenceFragment import io.github.wulkanowy.ui.modules.exam.ExamFragment import io.github.wulkanowy.ui.modules.homework.HomeworkFragment -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 @@ -67,9 +66,6 @@ class MoreFragment : BaseFragment(R.layout.fragment_more), override val examRes: Pair? get() = context?.run { getString(R.string.exam_title) to getCompatDrawable(R.drawable.ic_main_exam) } - override val luckyNumberRes: Pair? - get() = context?.run { getString(R.string.lucky_number_title) to getCompatDrawable(R.drawable.ic_more_lucky_number) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentMoreBinding.bind(view) @@ -132,10 +128,6 @@ class MoreFragment : BaseFragment(R.layout.fragment_more), (activity as? MainActivity)?.pushView(ExamFragment.newInstance()) } - override fun openLuckyNumberView() { - (activity as? MainActivity)?.pushView(LuckyNumberFragment.newInstance()) - } - override fun popView(depth: Int) { (activity as? MainActivity)?.popView(depth) } 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 92551d6e9..a2b7f204e 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 @@ -31,7 +31,6 @@ class MorePresenter @Inject constructor( schoolAndTeachersRes?.first -> openSchoolAndTeachersView() mobileDevicesRes?.first -> openMobileDevicesView() settingsRes?.first -> openSettingsView() - luckyNumberRes?.first -> openLuckyNumberView() } } } @@ -49,7 +48,6 @@ class MorePresenter @Inject constructor( examRes, homeworkRes, noteRes, - luckyNumberRes, conferencesRes, schoolAnnouncementRes, schoolAndTeachersRes, 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 cb895de28..c4a07bdcc 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 @@ -23,8 +23,6 @@ interface MoreView : BaseView { val examRes: Pair? - val luckyNumberRes: Pair? - fun initView() fun updateData(data: List>) @@ -48,6 +46,4 @@ interface MoreView : BaseView { fun openMobileDevicesView() fun openExamView() - - fun openLuckyNumberView() } 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 10a391820..c441231e0 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 @@ -11,6 +11,7 @@ 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 @@ -122,17 +123,15 @@ class NotePresenter @Inject constructor( } private fun updateNote(note: Note) { - 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!!) - } + 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") + }.launchIn(this) } } 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 27b3637ad..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterAdapter.kt +++ /dev/null @@ -1,47 +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.services.sync.notifications.NotificationType -import io.github.wulkanowy.utils.toFormattedString -import javax.inject.Inject - -class NotificationsCenterAdapter @Inject constructor() : - ListAdapter(DiffUtilCallback()) { - - var onItemClickListener: (NotificationType) -> 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.type) } - } - } - - 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 f3bbc42de..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt +++ /dev/null @@ -1,112 +0,0 @@ -package io.github.wulkanowy.ui.modules.notificationscenter - -import android.os.Bundle -import android.view.View -import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -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.services.sync.notifications.NotificationType -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment -import io.github.wulkanowy.ui.modules.conference.ConferenceFragment -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.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.ui.modules.message.MessageFragment -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 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 = { notificationType -> - notificationType.toDestinationFragment() - ?.let { (requireActivity() as MainActivity).pushView(it) } - } - - 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() - } - - private fun NotificationType.toDestinationFragment(): Fragment? = when (this) { - NotificationType.NEW_CONFERENCE -> ConferenceFragment.newInstance() - NotificationType.NEW_EXAM -> ExamFragment.newInstance() - NotificationType.NEW_GRADE_DETAILS -> GradeFragment.newInstance() - NotificationType.NEW_GRADE_PREDICTED -> GradeFragment.newInstance() - NotificationType.NEW_GRADE_FINAL -> GradeFragment.newInstance() - NotificationType.NEW_HOMEWORK -> HomeworkFragment.newInstance() - NotificationType.NEW_LUCKY_NUMBER -> LuckyNumberFragment.newInstance() - NotificationType.NEW_MESSAGE -> MessageFragment.newInstance() - NotificationType.NEW_NOTE -> NoteFragment.newInstance() - NotificationType.NEW_ANNOUNCEMENT -> SchoolAnnouncementFragment.newInstance() - NotificationType.PUSH -> null - NotificationType.CHANGE_TIMETABLE -> TimetableFragment.newInstance() - NotificationType.NEW_ATTENDANCE -> AttendanceFragment.newInstance() - } -} 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 394c23101..000000000 --- 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") } - .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 1bfbe75e1..000000000 --- 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 f4fa8e01d..c1c569611 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 43823d6b4..915cc4213 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 ac8c273ea..202d4e5d7 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 @@ -9,7 +9,6 @@ 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.catch import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -101,9 +100,6 @@ class SchoolPresenter @Inject constructor( enableSwipe(true) notifyParentDataLoaded() } - }.catch { - errorHandler.dispatch(it) - view?.notifyParentDataLoaded() }.launch() } @@ -115,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 bd46ff0b2..c83cfe766 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 @@ -9,7 +9,6 @@ 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.catch import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -86,9 +85,6 @@ class TeacherPresenter @Inject constructor( enableSwipe(true) notifyParentDataLoaded() } - }.catch { - errorHandler.dispatch(it) - view?.notifyParentDataLoaded() }.launch() } @@ -99,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/SchoolAnnouncementDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementDialog.kt index 7dcd51cea..ed4b0ac98 100644 --- 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 @@ -38,7 +38,7 @@ class SchoolAnnouncementDialog : DialogFragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ) = DialogSchoolAnnouncementBinding.inflate(inflater).also { binding = it }.root + ) = DialogSchoolAnnouncementBinding.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/schoolannouncement/SchoolAnnouncementPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt index d6a32e3cc..88ad81465 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 @@ -64,7 +64,6 @@ class SchoolAnnouncementPresenter @Inject constructor( view?.run { enableSwipe(true) showRefresh(true) - showErrorView(false) showProgress(false) showContent(true) updateData(it.data) 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 d56cdfa7c..2612fab3a 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,16 +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) {} } 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 79f91bc5d..000000000 --- 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 a2265b044..9f29731f6 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 @@ -27,6 +27,10 @@ class AdvancedFragment : PreferenceFragmentCompat(), @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?) { 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 f603de781..a7ee800be 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 @@ -27,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?) { 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 84fee7179..0fc7e68e7 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 @@ -10,12 +10,9 @@ import android.provider.Settings import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog -import androidx.core.app.NotificationManagerCompat import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import androidx.preference.SwitchPreferenceCompat import androidx.recyclerview.widget.RecyclerView import com.thelittlefireman.appkillermanager.AppKillerManager import com.thelittlefireman.appkillermanager.exceptions.NoActionFoundException @@ -40,27 +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 - override val isNotificationPermissionGranted: Boolean - get() { - val packageNameList = - NotificationManagerCompat.getEnabledListenerPackages(requireContext()) - val appPackageName = requireContext().packageName - - return appPackageName in packageNameList - } - - 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 @@ -75,11 +57,12 @@ 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( @@ -141,7 +124,7 @@ class NotificationsFragment : PreferenceFragmentCompat(), .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()) @@ -174,44 +157,6 @@ class NotificationsFragment : PreferenceFragmentCompat(), } } - override fun openNotificationPermissionDialog() { - AlertDialog.Builder(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() { - AlertDialog.Builder(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 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 - } - override fun onResume() { super.onResume() preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(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 4cbdac945..8366d3094 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 @@ -31,9 +31,6 @@ class NotificationsPresenter @Inject constructor( ) initView(appInfo.isDebug) } - - checkNotificationPiggybackState() - Timber.i("Settings notifications view was initialized") } @@ -42,21 +39,14 @@ class NotificationsPresenter @Inject constructor( preferencesRepository.apply { when (key) { - isUpcomingLessonsNotificationsEnableKey, isUpcomingLessonsNotificationsPersistentKey -> { + isUpcomingLessonsNotificationsEnableKey -> { if (!isUpcomingLessonsNotificationsEnable) { timetableNotificationHelper.cancelNotification() - } else if (!timetableNotificationHelper.canScheduleExactAlarms()) { - view?.openNotificationExactAlarmSettings() } } isDebugNotificationEnableKey -> { chuckerCollector.showNotification = isDebugNotificationEnable } - isNotificationPiggybackEnabledKey -> { - if (isNotificationPiggybackEnabled && view?.isNotificationPermissionGranted == false) { - view?.openNotificationPermissionDialog() - } - } } } analytics.logEvent("setting_changed", "name" to key) @@ -69,22 +59,4 @@ class NotificationsPresenter @Inject constructor( fun onOpenSystemSettingsClicked() { view?.openSystemSettings() } - - fun onNotificationPiggybackPermissionResult() { - view?.run { - setNotificationPiggybackPreferenceChecked(isNotificationPermissionGranted) - } - } - - fun onNotificationExactAlarmPermissionResult() { - view?.setUpcomingLessonsNotificationPreferenceChecked(timetableNotificationHelper.canScheduleExactAlarms()) - } - - private fun checkNotificationPiggybackState() { - if (preferencesRepository.isNotificationPiggybackEnabled) { - view?.run { - setNotificationPiggybackPreferenceChecked(isNotificationPermissionGranted) - } - } - } } 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 2bf8e31f4..2ab9b0352 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,8 +4,6 @@ import io.github.wulkanowy.ui.base.BaseView interface NotificationsView : BaseView { - val isNotificationPermissionGranted: Boolean - fun initView(showDebugNotificationSwitch: Boolean) fun showFixSyncDialog() @@ -13,12 +11,4 @@ interface NotificationsView : BaseView { fun openSystemSettings() fun enableNotification(notificationKey: String, enable: Boolean) - - fun openNotificationPermissionDialog() - - fun openNotificationExactAlarmSettings() - - 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 160b7c37b..83caa3b0a 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 @@ -20,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) 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 0d404a138..63e86a475 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") @@ -63,9 +63,9 @@ class SyncPresenter @Inject constructor( ) 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() } 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 5c1524551..376ef3743 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,54 +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 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" - - private const val EXTRA_EXTERNAL_URL = "external_url" - - fun getStartIntent(context: Context, destination: Destination? = null) = - Intent(context, SplashActivity::class.java).apply { - putExtra(EXTRA_START_DESTINATION, destination) - 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().setKeepVisibleCondition { true } - - val externalLink = intent?.getStringExtra(EXTRA_EXTERNAL_URL) - val startDestination = intent?.getSerializableExtra(EXTRA_START_DESTINATION) as Destination? - ?: shortcutsHelper.getDestination(intent) - - presenter.onAttachView(this, externalLink, startDestination) + presenter.onAttachView(this, intent?.getStringExtra("external_url")) } override fun openLoginView() { @@ -56,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 0b7409020..03e43efa8 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,10 +1,12 @@ 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 io.github.wulkanowy.utils.flowWithResource +import kotlinx.coroutines.flow.onEach +import timber.log.Timber import javax.inject.Inject class SplashPresenter @Inject constructor( @@ -12,7 +14,7 @@ class SplashPresenter @Inject constructor( studentRepository: StudentRepository, ) : BasePresenter(errorHandler, studentRepository) { - fun onAttachView(view: SplashView, externalUrl: String?, startDestination: Destination?) { + fun onAttachView(view: SplashView, externalUrl: String?) { super.onAttachView(view) if (!externalUrl.isNullOrBlank()) { @@ -20,16 +22,15 @@ class SplashPresenter @Inject constructor( 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 1c5d8bfd4..a5aa14091 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/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt index 2228aaf46..87b3362db 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,13 +1,12 @@ package io.github.wulkanowy.ui.modules.timetable import android.graphics.Paint -import android.os.Handler -import android.os.Looper 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.ViewCompat import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R @@ -152,8 +151,8 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter { @@ -89,26 +90,20 @@ class TimetableDialog : DialogFragment() { R.attr.colorPrimary ) ) - timetableDialogChangesValue.setTextColor( - requireContext().getThemeAttrColor( - 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() } } @@ -136,15 +131,6 @@ class TimetableDialog : DialogFragment() { } } } - teacherOld.isNotBlank() && teacherOld == teacher -> { - timetableDialogTeacherValue.run { - visibility = GONE - } - timetableDialogTeacherNewValue.run { - visibility = VISIBLE - text = teacher - } - } teacher.isNotBlank() -> timetableDialogTeacherValue.text = teacher else -> { timetableDialogTeacherTitle.visibility = GONE 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 07a9f6c76..1e1084a8d 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 @@ -44,13 +44,7 @@ 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 = Bundle().apply { - date?.let { putLong(ARGUMENT_DATE_KEY, it.toEpochDay()) } - } - } + fun newInstance() = TimetableFragment() } override val titleStringId get() = R.string.timetable_title @@ -68,11 +62,7 @@ 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() { @@ -97,7 +87,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme timetableNavDate.setOnClickListener { presenter.onPickDate() } timetableNextButton.setOnClickListener { presenter.onNextDay() } - timetableNavContainer.elevation = requireContext().dpToPx(8f) + timetableNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -212,9 +202,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth) } - if (!parentFragmentManager.isStateSaved) { - datePicker.show(parentFragmentManager, null) - } + datePicker.show(this@TimetableFragment.parentFragmentManager, null) } override fun openAdditionalLessonsView() { 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 86e993982..fa1bbfb28 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 @@ -149,7 +149,6 @@ class TimetablePresenter @Inject constructor( view?.run { enableSwipe(true) showRefresh(true) - showErrorView(false) showProgress(false) showContent(true) updateData(it.data!!.lessons) 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 e8fb9e440..a4e1f0fc0 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 @@ -72,7 +72,7 @@ class AdditionalLessonsFragment : additionalLessonsNavDate.setOnClickListener { presenter.onPickDate() } additionalLessonsNextButton.setOnClickListener { presenter.onNextDay() } - additionalLessonsNavContainer.elevation = requireContext().dpToPx(8f) + additionalLessonsNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -152,9 +152,7 @@ class AdditionalLessonsFragment : presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth) } - if (!parentFragmentManager.isStateSaved) { - datePicker.show(parentFragmentManager, null) - } + datePicker.show(this@AdditionalLessonsFragment.parentFragmentManager, null) } override fun onSaveInstanceState(outState: Bundle) { 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 36e38fb96..00ba0bad8 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 a6b126447..ad698c1cf 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 @@ -79,7 +79,7 @@ class CompletedLessonsFragment : completedLessonsNavDate.setOnClickListener { presenter.onPickDate() } completedLessonsNextButton.setOnClickListener { presenter.onNextDay() } - completedLessonsNavContainer.elevation = requireContext().dpToPx(8f) + completedLessonsNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } } @@ -173,9 +173,7 @@ class CompletedLessonsFragment : presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth) } - if (!parentFragmentManager.isStateSaved) { - datePicker.show(parentFragmentManager, null) - } + datePicker.show(this@CompletedLessonsFragment.parentFragmentManager, null) } override fun onSaveInstanceState(outState: Bundle) { 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 c8dda23ce..45b79b50f 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 @@ -173,7 +173,7 @@ class TimetableWidgetFactory( updateNotCanceledLessonNumberColor(this, lesson) updateNotCanceledSubjectColor(this, lesson) - val teacherChange = lesson.teacherOld.isNotBlank() + val teacherChange = lesson.teacherOld.isNotBlank() && lesson.teacher != lesson.teacherOld updateNotCanceledRoom(this, lesson, teacherChange) updateNotCanceledTeacher(this, lesson, teacherChange) } 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 0f0691160..f9079b5f9 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 @@ -2,6 +2,7 @@ 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.ACTION_APPWIDGET_DELETED import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE @@ -23,10 +24,9 @@ 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.ui.modules.main.MainActivity +import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.AnalyticsHelper -import io.github.wulkanowy.utils.PendingIntentCompat import io.github.wulkanowy.utils.capitalise import io.github.wulkanowy.utils.createNameInitialsDrawable import io.github.wulkanowy.utils.getCompatColor @@ -60,8 +60,6 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() { 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" @@ -167,20 +165,18 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() { action = appWidgetId.toString() } val accountIntent = PendingIntent.getActivity( - context, - -Int.MAX_VALUE + appWidgetId, + 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 - + }, 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 remoteView = RemoteViews(context.packageName, layoutId).apply { @@ -224,16 +220,16 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() { 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 - ) + ): 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) 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 6b7fb4aa9..0f121dc5b 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 4e1ca1a97..a04922e5d 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 000000000..e19d01116 --- /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 000000000..eb5cae4f3 --- /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 962e5b208..a3961aed8 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 397c95953..479cc5188 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/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt index ecd982a18..68d3afe83 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -18,7 +18,6 @@ import androidx.annotation.AttrRes import androidx.annotation.ColorInt import androidx.annotation.ColorRes import androidx.annotation.DrawableRes -import androidx.annotation.PluralsRes import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils import androidx.core.graphics.applyCanvas @@ -58,9 +57,6 @@ 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 { try { @@ -106,9 +102,7 @@ fun Context.openNavigation(location: String) { 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) - } + startActivity(intent) } fun Context.shareText(text: String, subject: String?) { 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 8aaa57f4f..ecc8e05eb 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/FragNavControlerExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/FragNavControlerExtension.kt index 01c876dd2..9dc1e18a0 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 000000000..210a62090 --- /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 1be3093fe..820e7f435 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt @@ -3,7 +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.sdk.scrapper.grades.isGradeValid +import io.github.wulkanowy.sdk.scrapper.grades.* fun List.calcAverage(isOptionalArithmeticAverage: Boolean): Double { val isArithmeticAverage = isOptionalArithmeticAverage && !any { it.weightValue != .0 } @@ -18,7 +18,8 @@ fun List.calcAverage(isOptionalArithmeticAverage: Boolean): Double { return if (denominator != 0.0) counter / denominator else 0.0 } -fun List.calcFinalAverage(plusModifier: Double, minusModifier: Double) = asSequence() +@JvmName("calcSummaryAverage") +fun List.calcAverage(plusModifier: Double, minusModifier: Double) = asSequence() .mapNotNull { if (it.finalGrade.matches("[0-6][+-]?".toRegex())) { when { 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 032e2d28a..d2a8908ce 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt @@ -4,12 +4,13 @@ import android.os.Handler import android.os.Looper import androidx.appcompat.app.AppCompatActivity 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 @@ -22,15 +23,15 @@ 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 LifecycleAwareVariableActivity : ReadWriteProperty, - DefaultLifecycleObserver { + LifecycleObserver { private var _value: T? = null @@ -43,7 +44,9 @@ class LifecycleAwareVariableActivity : ReadWriteProperty) = _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 } 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 1e9f49a66..ee18453ff 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/PendingIntentCompat.kt b/app/src/main/java/io/github/wulkanowy/utils/PendingIntentCompat.kt deleted file mode 100644 index 45ee431a2..000000000 --- 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 6bf97bae7..cd59b8648 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt @@ -33,7 +33,7 @@ class AutoRefreshHelper @Inject constructor( private val sharedPref: SharedPrefProvider ) { - fun shouldBeRefreshed(key: String): Boolean { + 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() diff --git a/app/src/main/java/io/github/wulkanowy/utils/SchooldaysValidator.kt b/app/src/main/java/io/github/wulkanowy/utils/SchooldaysValidator.kt index b6dd528f5..00fccfc8d 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/SchooldaysValidator.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/SchooldaysValidator.kt @@ -2,6 +2,7 @@ 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 @@ -11,6 +12,7 @@ class SchoolDaysValidator(val start: Long, val end: Long) : CalendarConstraints. val date = dateLong.toLocalDateTime() return date.until(end.toLocalDateTime(), ChronoUnit.DAYS) >= 0 && - date.until(start.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/StringExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt index bddd7df4c..5c888f30a 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt @@ -4,4 +4,6 @@ inline fun String?.ifNullOrBlank(defaultValue: () -> String) = if (isNullOrBlank()) defaultValue() else this fun String.capitalise() = - replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } \ No newline at end of file + replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } + +fun String.decapitalise() = replaceFirstChar { it.lowercase() } 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 700ac2f1d..6a5ad880a 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 c994ebab6..74ae19326 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 1420f5d67..000000000 --- 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 0f29ab1b5..000000000 --- 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 b7f42a5b0..000000000 --- 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 2a4787d2d..000000000 --- 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 645ebbb6a..000000000 --- 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 aa50ce77a..000000000 --- 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 82289ffc9..aeef2a77e 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,6 +1,7 @@ -Wersja 1.4.3 +Wersja 1.2.1 -Naprawiliśmy drobne problemy ze stabilnością oraz małą usterkę w wyszukiwarce wiadomości. -Wszystko po to, aby jeszcze bardziej podnieść komfort z używania aplikacji! +- dodaliśmy brakujące okienka z podglądem szczegółów ogłoszeń szkolnych +- naprawiliśmy rzucające się w oczy błędy na ekranie startowym +- naprawiliśmy też inne drobne błędy w wyglądzie i stabilności aplikacji Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases 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 dad56a171..000000000 --- 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 000000000..61489d81b 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 000000000..1b4b64ec9 --- /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_luckynumber_widget.xml b/app/src/main/res/drawable/background_luckynumber_widget.xml index 367c55275..f29744d0c 100644 --- a/app/src/main/res/drawable/background_luckynumber_widget.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 index cb094b57e..fa15fd857 100644 --- a/app/src/main/res/drawable/background_luckynumber_widget_dark.xml +++ b/app/src/main/res/drawable/background_luckynumber_widget_dark.xml @@ -1,6 +1,6 @@ - - + + 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 e7a5dc5a4..000000000 --- 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_help.xml b/app/src/main/res/drawable/ic_help.xml deleted file mode 100644 index 9c6ba2925..000000000 --- a/app/src/main/res/drawable/ic_help.xml +++ /dev/null @@ -1,10 +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 c333ea763..000000000 --- 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 e2e747316..000000000 --- a/app/src/main/res/drawable/ic_splash_logo.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - 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 000000000..fb521bf65 Binary files /dev/null and b/app/src/main/res/drawable/img_splash_logo.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 000000000..2cf46d1d0 --- /dev/null +++ b/app/src/main/res/drawable/layer_splash_background.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 1d5b5280d..e55ea8b95 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -10,7 +10,7 @@ android:layout_height="wrap_content" android:background="@android:color/transparent" /> - 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 d08edf4f7..000000000 --- a/app/src/main/res/layout/dialog_conference.xml +++ /dev/null @@ -1,211 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_error.xml b/app/src/main/res/layout/dialog_error.xml index b52c99aca..a78790bce 100644 --- a/app/src/main/res/layout/dialog_error.xml +++ b/app/src/main/res/layout/dialog_error.xml @@ -4,8 +4,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:minWidth="300dp" - android:orientation="vertical" - tools:context=".ui.base.ErrorDialog"> + android:orientation="vertical"> - - diff --git a/app/src/main/res/layout/dialog_homework.xml b/app/src/main/res/layout/dialog_homework.xml index 341cec544..22a03cb21 100644 --- a/app/src/main/res/layout/dialog_homework.xml +++ b/app/src/main/res/layout/dialog_homework.xml @@ -4,7 +4,6 @@ android:id="@+id/parent" android:layout_width="match_parent" android:layout_height="match_parent" - android:minWidth="300dp" android:orientation="vertical"> + android:background="@drawable/ic_all_divider" + android:layout_height="1dp" /> @@ -53,9 +52,6 @@ style="@style/Widget.MaterialComponents.Button.TextButton.Dialog" android:layout_width="wrap_content" android:layout_height="36dp" - android:layout_alignParentEnd="true" - android:layout_alignParentBottom="true" - android:layout_gravity="center_vertical" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" @@ -64,6 +60,9 @@ android:insetRight="0dp" android:insetBottom="0dp" android:minWidth="88dp" + android:layout_gravity="center_vertical" + android:layout_alignParentEnd="true" + android:layout_alignParentBottom="true" android:text="@string/all_close" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" /> 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 b9b8d1a2e..000000000 --- a/app/src/main/res/layout/dialog_homework_add.xml +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_school_announcement.xml b/app/src/main/res/layout/dialog_school_announcement.xml index 96c11d4a4..cbce4e025 100644 --- a/app/src/main/res/layout/dialog_school_announcement.xml +++ b/app/src/main/res/layout/dialog_school_announcement.xml @@ -118,7 +118,6 @@ android:layout_marginEnd="24dp" android:paddingStart="0dp" android:paddingEnd="16dp" - tools:maxLines="5" android:text="@string/all_no_data" android:textIsSelectable="true" android:textSize="16sp" diff --git a/app/src/main/res/layout/fragment_attendance.xml b/app/src/main/res/layout/fragment_attendance.xml index 4996b85db..8016081b8 100644 --- a/app/src/main/res/layout/fragment_attendance.xml +++ b/app/src/main/res/layout/fragment_attendance.xml @@ -1,15 +1,14 @@ - + android:layout_height="match_parent" + android:layout_marginBottom="50dp"> + 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_exam.xml b/app/src/main/res/layout/fragment_exam.xml index 0c62aab5f..ca88849c5 100644 --- a/app/src/main/res/layout/fragment_exam.xml +++ b/app/src/main/res/layout/fragment_exam.xml @@ -128,8 +128,8 @@ android:paddingRight="12dp" android:paddingBottom="8dp" android:scaleType="fitStart" - app:srcCompat="@drawable/ic_chevron_left" - app:tint="?colorPrimary" /> + 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 989929d4c..ed0447fb5 100644 --- a/app/src/main/res/layout/fragment_grade.xml +++ b/app/src/main/res/layout/fragment_grade.xml @@ -17,7 +17,7 @@ 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_height="wrap_content" + app:layout_constraintTop_toTopOf="parent" + tools:listitem="@layout/item_grade_statistics_pie" + tools:visibility="visible" /> - + - + android:layout_height="wrap_content"> - - - + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="vertical" + android:visibility="invisible" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:ignore="UseCompoundDrawables" + tools:visibility="gone"> + + + + + + + + + + + + android:orientation="horizontal"> - - - - + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" + android:text="@string/all_details" /> - - - - - - - - - - - - + android:text="@string/all_retry" /> - - - - - - + + + + + + diff --git a/app/src/main/res/layout/fragment_homework.xml b/app/src/main/res/layout/fragment_homework.xml index ae8270ab1..c0b5698d5 100644 --- a/app/src/main/res/layout/fragment_homework.xml +++ b/app/src/main/res/layout/fragment_homework.xml @@ -26,8 +26,6 @@ android:id="@+id/homeworkRecycler" android:layout_width="match_parent" android:layout_height="match_parent" - android:clipToPadding="false" - android:paddingBottom="64dp" tools:listitem="@layout/item_homework" /> @@ -107,18 +105,6 @@ android:text="@string/all_retry" /> - - + 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_form.xml b/app/src/main/res/layout/fragment_login_form.xml index d1c997ff8..06d1fa5e9 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -110,8 +110,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="32dp" + android:layout_marginLeft="32dp" android:layout_marginTop="32dp" android:layout_marginEnd="32dp" + android:layout_marginRight="32dp" android:gravity="center_horizontal" android:text="@string/login_header_default" android:textSize="16sp" @@ -124,20 +126,6 @@ app:layout_constraintVertical_chainStyle="packed" app:layout_goneMarginTop="64dp" /> - + app:layout_constraintTop_toBottomOf="@+id/loginFormHeader"> @@ -227,6 +217,7 @@ android:layout_marginRight="24dp" android:hint="@string/login_host_hint" android:orientation="vertical" + app:layout_constraintBottom_toTopOf="@+id/loginFormAdvancedButton" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginFormRecoverLink"> @@ -271,13 +262,14 @@ android:id="@+id/loginFormPrivacyLink" android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_marginTop="24dp" android:gravity="start|center_vertical" android:text="@string/login_privacy_policy" android:textColor="?android:textColorSecondary" android:textSize="12sp" app:fontFamily="sans-serif-medium" app:layout_constraintStart_toStartOf="@id/loginFormAdvancedButton" - app:layout_constraintTop_toTopOf="@+id/loginFormVersion" + app:layout_constraintTop_toBottomOf="@+id/loginFormAdvancedButton" 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_message.xml b/app/src/main/res/layout/fragment_message.xml index 5269d95e8..a61f37381 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"> - - 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 f59ce33c9..000000000 --- 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 3f5dc6e23..32be61db8 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" /> - - + 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 a71f7545b..61eb44454 100644 --- a/app/src/main/res/layout/fragment_timetable_additional.xml +++ b/app/src/main/res/layout/fragment_timetable_additional.xml @@ -131,8 +131,8 @@ android:paddingRight="12dp" android:paddingBottom="8dp" android:scaleType="fitStart" - app:srcCompat="@drawable/ic_chevron_left" - app:tint="?colorPrimary" /> + 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 e089275d9..1a890fe12 100644 --- a/app/src/main/res/layout/fragment_timetable_completed.xml +++ b/app/src/main/res/layout/fragment_timetable_completed.xml @@ -130,8 +130,8 @@ android:paddingRight="12dp" android:paddingBottom="8dp" android:scaleType="fitStart" - app:srcCompat="@drawable/ic_chevron_left" - app:tint="?colorPrimary" /> + 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_admin_message.xml b/app/src/main/res/layout/item_dashboard_admin_message.xml deleted file mode 100644 index 67836561b..000000000 --- a/app/src/main/res/layout/item_dashboard_admin_message.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - \ 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 9156c1a2f..a2a92c54c 100644 --- a/app/src/main/res/layout/item_dashboard_lessons.xml +++ b/app/src/main/res/layout/item_dashboard_lessons.xml @@ -42,19 +42,6 @@ app:layout_constraintBottom_toBottomOf="@id/dashboard_lessons_item_title" app:layout_constraintStart_toEndOf="@id/dashboard_lessons_item_title" /> - - + android:orientation="horizontal" > - - + diff --git a/app/src/main/res/layout/item_notifications_center.xml b/app/src/main/res/layout/item_notifications_center.xml deleted file mode 100644 index 16a7ae0c0..000000000 --- a/app/src/main/res/layout/item_notifications_center.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/scrollable_header_about.xml b/app/src/main/res/layout/scrollable_header_about.xml index 5a7669fdf..e203d98d4 100644 --- a/app/src/main/res/layout/scrollable_header_about.xml +++ b/app/src/main/res/layout/scrollable_header_about.xml @@ -6,7 +6,6 @@ android:layout_height="wrap_content" android:minHeight="104dp" android:orientation="vertical" - android:paddingHorizontal="20dp" tools:context=".ui.modules.about.AboutAdapter"> + app:layout_constraintTop_toTopOf="@id/aboutScrollableHeaderIcon" /> diff --git a/app/src/main/res/layout/scrollable_header_grade_summary.xml b/app/src/main/res/layout/scrollable_header_grade_summary.xml index 049219a98..29657ba1a 100644 --- a/app/src/main/res/layout/scrollable_header_grade_summary.xml +++ b/app/src/main/res/layout/scrollable_header_grade_summary.xml @@ -1,6 +1,5 @@ - - - - - - + android:gravity="center_horizontal" + android:textSize="21sp" + tools:text="6,00" /> - - - - - - + android:gravity="center_horizontal" + android:textSize="21sp" + tools:text="6,00" /> + android:layout_height="wrap_content"> + tools:text="Urządzenia techniki kompu..." /> diff --git a/app/src/main/res/menu/action_menu_dashboard.xml b/app/src/main/res/menu/action_menu_dashboard.xml index 13565a196..dbdd6e812 100644 --- a/app/src/main/res/menu/action_menu_dashboard.xml +++ b/app/src/main/res/menu/action_menu_dashboard.xml @@ -1,17 +1,10 @@ - diff --git a/app/src/main/res/menu/action_menu_main.xml b/app/src/main/res/menu/action_menu_main.xml index f14d1f749..219059391 100644 --- a/app/src/main/res/menu/action_menu_main.xml +++ b/app/src/main/res/menu/action_menu_main.xml @@ -5,7 +5,7 @@ diff --git a/app/src/main/res/values-cs/preferences_values.xml b/app/src/main/res/values-cs/preferences_values.xml index 5252f79b8..fb938f092 100644 --- a/app/src/main/res/values-cs/preferences_values.xml +++ b/app/src/main/res/values-cs/preferences_values.xml @@ -40,20 +40,15 @@ Wulkanowy Barvy známek v deníku - - Až 1 najednou - Vždy rozbalené - Neomezené rozbalené - - Průměr známek pouze z vybraného semestru - Průměr z průměrů z obou semestrů + Průměrná známka od druhého semestru + Průměr známek z obou semestrů Průměr známek z celého roku Šťastné číslo Nepřečtené zprávy - Frekvence + Docházka Lekce Známky Domácí úkoly diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index e8a352152..fedbc9037 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -4,7 +4,7 @@ Přihlášení Wulkanowy Známky - Frekvence + Docházka Zkoušky Plán lekce Nastavení @@ -12,12 +12,11 @@ O aplikaci Prohlížeč protokolů Ladění - Ladění upozornění + Ladění oznámení Tvůrci Licence Zprávy Nová zpráva - Nový domácí úkol Poznámky a úspěchy Domácí úkoly Manažer účtů @@ -25,12 +24,11 @@ Podrobnosti účtu Informace o žáku Domů - Centrum upozornění Semestr %1$d, %2$d/%3$d Přihlaste se pomocí studentského nebo rodičovského účtu - Zadejte symbol ze stránky deníku: <b>%1$s</b> + Zadejte symbol ze stránky deníku Uživatelské jméno Email Přihlášení, číslo PESEL nebo e-mail @@ -44,8 +42,7 @@ Symbol Přihlásit Toto heslo je příliš krátké - Přihlašovací údaje jsou nesprávné - %1$s. Zkontrolujte, zda je níže vybrána správná variace deníku UONET+ + Přihlašovací údaje jsou nesprávné. Ujistěte se, že je v poli níže vybrána správná variace deníku UONET+ Neplatný PIN Neplatný token Token vypršel @@ -54,14 +51,15 @@ Použijte přiřazené přihlašovací nebo e-mail v @%1$s Neplatný symbol Žák nebyl nalezen. Zkontrolujte správnost symbolu a vybrané varianty deníku UONET+ + Toto pole je povinné Vybraný žák je už přihlášen Symbol najdete na stránce deníku v  Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUjistěte se, že jste na předchozí obrazovce nastavili správnou variantu deníku do pole Variace deníku UONET+. Wulkanowy v tuto chvíli nezjistí předškolní żaków Vyberte žáky, kteří se mají do aplikace přihlásit Jiné možnosti - V tomto režimu nefungují následující: šťastné číslo, statistiky třídy, shrnutí frekvencí, ospravedlnění nepřítomnosti, dokončené lekce, informace o škole a prohlížení seznamu registrovaných zařízení + V tomto režimu nefungují následující: šťastné číslo, statistiky třídy, shrnutí docházky, ospravedlnění nepřítomnosti, dokončené lekce, informace o škole a prohlížení seznamu registrovaných zařízení Tento režim zobrazuje stejná data, která se zobrazují na webových stránkách deníka Kombinace nejlepších vlastností ostatních dvou režimů. Funguje rychleji než scraper a poskytuje funkce, které nejsou k dispozici v režimu Mobile API. Je to v experimentální fázi - Ochrana osobních údajů + Zásady ochrany osobních údajů Problémy s přihlášením? Napište nám! Email Discord @@ -92,10 +90,6 @@ Konečná známka Předpokládaná známka Vypočítaný průměr - Jak funguje vypočítaný průměr? - Vypočítaný průměr je aritmetický průměr vypočítaný z průměrů předmětů. Umožňuje vám to znát přibližný konečný průměr. Vypočítává se způsobem zvoleným uživatelem v nastavení aplikaci. Doporučuje se vybrat příslušnou možnost. Důvodem je rozdílný výpočet školních průměrů. Pokud vaše škola navíc uvádí průměr předmětů na stránce deníku Vulcan, aplikace si je stáhne a tyto průměry nepočítá. To lze změnit vynucením výpočtu průměru v nastavení aplikaci.\n\nPrůměr známek pouze z vybraného semestru:\n1. Výpočet váženého průměru pro každý předmět v daném semestru\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů\n\nPrůměr průměrů z obou semestrů:\n1. Výpočet váženého průměru pro každý předmět v semestru 1 a 2\n2. Výpočet aritmetického průměru vypočítaných průměrů za semestry 1 a 2 pro každý předmět.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru součtených průměrů\n\nPrůměr známek z celého roku:\n1. Výpočet váženého průměru za rok pro každý předmět. Konečný průměr v 1. semestru je nepodstatný.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru součtených průměrů - Jak funguje konečný průměr? - Konečný průměr je aritmetický průměr vypočítaný ze všech aktuálně dostupných konečných známek v daném semestru.\n\nSchéma výpočtu se skládá z následujících kroků:\n1. Sčítání konečných známek zadaných učiteli\n2. Děleno počtem předmětů, pro které už byly vydány známky Konečný průměr z %1$d z %2$d předmětů Shrnutí @@ -165,34 +159,6 @@ Teď: %s Za chvíli: %s Později: %s - %1$s lekce %2$d - %3$s - Změna učebny z %1$s na %2$s - Změna učitele z %1$s na %2$s - Změna předmětu z %1$s na %2$s - - Změna plánu lekcí - Změny plánu lekcí - Změny plánu lekcí - Změny plánu lekcí - - - %1$s - %2$d změna plánu lekcí - %1$s - %2$d změny plánu lekcí - %1$s - %2$d změn plánu lekcí - %1$s - %2$d změn plánu lekcí - - - %1$d změna plánu lekcí - %1$d změny plánu lekcí - %1$d změn plánu lekcí - %1$d změn plánu lekcí - - - %d změna - %d změny - %d změn - %d změn - Dokončené lekce Zobrazit dokončené lekce @@ -205,7 +171,7 @@ Zobrazit další lekce Žádné informace o dalších lekcích - Shrnutí frekvencí + Shrnutí docházky Neprítomnosť zo školských dôvodov Omluvená nepřítomnost Neomluvená nepřítomnost @@ -222,24 +188,6 @@ Žádost o omluvu nepřítomnosti byla úspěšně odeslána! Musíte vybrat alespoň jednu nepřítomnost! Ospravedlnit - - Nové frekvence - Nové frekvence - Nové frekvence - Nové frekvence - - - %1$d nové frekvence - %1$d nové frekvence - %1$d nových frekvencí - %1$d nových frekvencí - - - %d frekvence - %d frekvence - %d frekvencí - %d frekvencí - Společně @@ -290,7 +238,12 @@ Pouze nepřečtené Pouze s přílohami Přečtena: %s - Přečtena přes: %1$d z %2$d osob + + Přečtena přes: %1$d z %2$d osob + Přečtena přes: %1$d z %2$d osob + Přečtena přes: %1$d z %2$d osob + Přečtena přes: %1$d z %2$d osob + %d zpráva %d zprávy @@ -374,9 +327,6 @@ Žádné informace o domácích úkolech Označit jako hotové Neudělané - Přidat domácí úkol - Domácí úkol byl úspěšně přidán - Domácí úkol byl úspěšně odstraněn Přílohy Nový domácí úkol @@ -452,8 +402,6 @@ Máte %1$d nových setkání Máte %1$d nových setkání - Přítomnost na setkání - Agenda Školní oznámení Žádná školní oznámení @@ -499,11 +447,11 @@ Přečtěte si často kladené otázky Server Discord Připojte se ke komunitě Wulkanového - Stránka na Facebooku + Facebooková fanpage Twitter stránka Sledujte nás na Twitteru - Dejte like naší stránce na Facebooku - Ochrana osobních údajů + Stejně jako naše facebooková fanpage + Zásady ochrany osobních údajů Pravidla pro shromažďování osobních údajů Systemová nastavení Otevřít systémová nastavení @@ -548,7 +496,6 @@ Lekce (Zítra) - (Dnes a zítra) Za chvíli: Brzy: První: @@ -569,10 +516,10 @@ Další: Později: - ještě %1$d lekce - ještě %1$d lekce - ještě %1$d lekcí - ještě %1$d lekcí + ještě %1$d další lekce + ještě %1$d další lekce + ještě %1$d dalších lekcí + ještě %1$d dalších lekcí do %1$s Žádné nadcházející lekce @@ -581,10 +528,10 @@ Žádné domácí úkoly do vykonána Při načítání domácích úkolů došlo k chybě - Ještě %1$d domácí úkol - Ještě %1$d domácí úkoly - Ještě %1$d domácích úkolů - Ještě %1$d domácích úkolů + Ještě %1$d další domácí úkol + Ještě %1$d další domácí úkoly + Ještě %1$d dalších domácích úkolů + Ještě %1$d dalších domácích úkolů do %1$s Poslední známky @@ -594,28 +541,28 @@ Žádná aktuální oznámení Při načítání oznámení došlo k chybě - Ještě %1$d oznámení - Ještě %1$d oznámení - Ještě %1$d oznámení - Ještě %1$d oznámení + Ještě %1$d další oznámení + Ještě %1$d další oznámení + Ještě %1$d dalších oznámení + Ještě %1$d dalších oznámení Zkoušky Žádné nadcházející zkoušky Při načítání zkoušek došlo k chybě - Ještě %1$d zkouška - Ještě %1$d zkoušky - Ještě %1$d zkoušek - Ještě %1$d zkoušek + Ještě %1$d další zkouška + Ještě %1$d další zkoušky + Ještě %1$d dalších zkoušek + Ještě %1$d dalších zkoušek Setkání Žádná nadcházející setkání Při načítání setkání došlo k chybě - Ještě %1$d setkání - Ještě %1$d setkání - Ještě %1$d setkání - Ještě %1$d setkání + Ještě %1$d další setkání + Ještě %1$d další setkání + Ještě %1$d dalších setkání + Ještě %1$d dalších setkání Při načítání dat došlo k chybě Žádné @@ -643,11 +590,6 @@ Ano Ne Uložit - Titul - Přidat - Zkopírováno - Vrátit - Změnit Žádné lekce Vybrat motiv @@ -655,13 +597,13 @@ Tmavý Motiv systému - Aplikace + Vzhled a chování aplikací Výchozí zobrazení - Možnosti vypočítaného průměru + Výpočet koncoročního průměru Vynutit průměrný výpočet podle aplikace Zobrazit přítomnost Motiv - Rozvíjení známek + Rozbalit známky Označit aktuální lekci Zobrazit skupiny vedle předmětů Zobrazit seznam grafů v známkách třídy @@ -670,22 +612,14 @@ Třídění předmětů Jazyk Upozornění - Jiné Zobrazit upozornění Zobrazit upozornění o nadcházející lekci - Nastavit upozornění o nadcházející lekci jako trvalé - Vypnout, když upozornění není ve vašem hodinkách/náramku viditelné Otevřít systémová nastavení upozornění Opravte problémy se synchronizací a upozorněním Vaše zařízení může mít problémy se synchronizací dat as upozorněními.\n\nChcete-li je opravit, přidejte Wulkanového do funkce Autostart a vypněte optimalizaci/úsporu baterie v nastavení systému telefonu. + Přejít do nastavení Zobrazit upozornění o ladění Synchronizace je vypnutá - Zachytit upozornění oficiální aplikací - Zachytit upozornění - S touto funkcí můžete získat náhradu push upozornění jako v oficiální aplikaci. Vše, co musíte udělat, je povolit Wulkanowému číst všechna vaše upozornění v nastaveních systému.\n\nJak to funguje?\nKdyž obdržíte oznámení v Deníčku VULCAN, Wulkanowy bude o tom informován (k tomu je to dodatečné povolení) a spustí synchronizaci, aby mohl zaslat vlastní upozornění.\n\nPOUZE PRO POKROČILÉ UŽIVATELE - Upozornění o nadcházející lekci - Musíte povolit Wulkanovému nastavit budíky a připomenutí v nastavení vašeho systému pro použití této funkce. - Přejít do nastavení Synchronizace Automatická aktualizace Pozastaveno na dovolené @@ -700,26 +634,16 @@ Hodnota mínusu Odpovědět s historií zpráv Vypočítat aritmetický průměr, pokud žádná známka nemá váhu - Podpora - Podívejte se na jednu reklamu pro podporu projektu - Souhlas se zpracováním dat - Jestli chcete sledovat reklamu, musíte souhlasit s podmínkami zpracování údajů v našich Zásadách Ochrany Osobních Údajů - Souhlasím - Ochrana osobních údajů - Reklama se načítá - Děkujeme za vaši podporu, vraťte se později pro více reklam Pokročilé Vzhled a chování Upozornění Synchronizace - Reklamy Známky Domů Viditelnost dlaždic - Frekvence + Docházka Plán lekce Známky - Vypočítaný průměr Zprávy Vzhled a chování Jazyky, motivy, třídění předmětů @@ -729,8 +653,7 @@ Automatická aktualizace, interval aktualizací Hodnota plusu a mínusu, výpočet průměru Pokročilé - Verze aplikace, tvůrci, sociální portály - Zobrazování reklam, podpora projektu + Verze aplikace, tvůrci, sociální portály, licence Nové známky Nové domácí úkoly @@ -743,8 +666,6 @@ Push upozornění Nadcházející lekce Ladění - Změny plánu lekcí - Nové frekvence Černá Červená @@ -752,6 +673,10 @@ Zelená Fialová Žádná barva + + Zkopírováno + Vrátit + Změnit Stahování aktualizací začalo… Aktualizace byla stažena. @@ -768,5 +693,4 @@ Vyskytla se neočekávaná chyba Funkce je deaktivována přes vaší školou Funkce není k dispozici. Přihlaste se v jiném režimu než Mobile API - Toto pole je povinné diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml index 23e54cb78..1e0df8dea 100644 --- a/app/src/main/res/values-de/preferences_values.xml +++ b/app/src/main/res/values-de/preferences_values.xml @@ -40,14 +40,9 @@ Wulkanowy Farben der Bewertungen im Logbuch - - Up to 1 at once - Always expanded - Unlimited expansions - + Durchschnittsnote für das 2. Semester Durchschnitt der Noten aus beiden Semestern - Durchschnittswert der Durchschnittswerte beider Semester Durchschnitt der Noten aus dem ganzen Jahr diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 6b6b5399b..5f14a4258 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -6,7 +6,7 @@ Noten Schulbesuch Prüfungen - Stundenplan + Zeitplan Einstellungen Mehr Über die Applikation @@ -17,7 +17,6 @@ Lizenzen Nachrichten neue Nachricht - New homework Eintragen und Erfolgen Hausaufgaben Konten-Manager @@ -25,12 +24,11 @@ Kontodetails Schülerinfo Übersicht - Benachrichtigungszentrum Semester %1$d, %2$d/%3$d Melden Sie sich mit dem Studenten- oder Elternkonto an - Geben Sie das Symbol von der Registerseite ein: <b>%1$s</b> + Geben Sie das Symbol von der Registerseite ein Benutzername Email Anmeldung, PESEL oder e-mail @@ -44,8 +42,7 @@ Symbol Anmelden Passwort ist zu kurz - Anmeldedaten sind falsch - %1$s. Stellen Sie sicher, dass die korrekte UONET+ Registervariation unten ausgewählt ist + Anmeldedaten sind falsch. Stellen Sie sicher, dass die richtige UONET+ Registervariation im unteren Feld ausgewählt ist Ungültige PIN Ungültige token Token ist nicht mehr gültig @@ -54,6 +51,7 @@ Benutze den zugewiesenen Login oder E-Mail in @%1$s Ungültige symbol Schüler nicht gefunden. Überprüfen Sie das Symbol und die gewählte Variation des UONET+ Registers + Dieses Datenfeld ist erforderlich Ausgewählter Student ist bereits angemeldet. Das Symbol kann auf der Registerseite in Uczeń→ Dostęp Mobilny → Zarejestruj urządzenie mobilnegefunden werden.\n\nStellen Sie sicher, dass Sie die entsprechende Registervariante im Feld UONET+ Registervariante auf dem vorherigen Bildschirm festgelegt haben. Wulkanowy erkennt zur Zeit keine Vorschulstudenten Wählen Sie die Studenten aus, die sich bei der Anwendung anmelden sollen. @@ -92,10 +90,6 @@ Finaler Note Vorhergesagte Note Berechnender Durchschnitt - Wie funktioniert der berechnete Durchschnitt? - The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n3. Adding calculated averages\n4. Calculating the arithmetic average of summed averages - Wie funktioniert der endgültige Durchschnitt? - The Final Average is the arithmetic average calculated from all currently available final grades in the given semester.\n\nThe calculation scheme consists of the following steps:\n1. Summing up the final grades given by teachers\n2. Divide by the number of subjects that have already been graded Finaler Durchschnitt aus %1$d von %2$d Schulfächern Zusammenfassung @@ -151,26 +145,6 @@ Jetzt: %s In einem Moment: %s Später: %s - %1$s lesson %2$d - %3$s - Change of room from %1$s to %2$s - Change of teacher from %1$s to %2$s - Change of subject from %1$s to %2$s - - Timetable change - Timetable changes - - - %1$s - %2$d change in timetable - %1$s - %2$d changes in timetable - - - %1$d change in timetable - %1$d changes in timetable - - - %d change - %d changes - Beendete Lektionen Beendete Lektionen anzeigen @@ -181,7 +155,7 @@ Zusätzliche Lektionen Zusätzliche Lektionen anzeigen - Keine Informationen über zusätzlichen Lektionen + Keine Infos zu zusätzlichen Lektionen Übersicht über die Schulbesuch Aus schulischen Gründen abwesend @@ -200,18 +174,6 @@ Abwesenheitsentschuldigungsanfrage erfolgreich gesendet! Sie müssen mindestens eine Abwesenheit auswählen! Verzeihung - - New attendance - New attendance - - - %1$d new attendance - %1$d attendance - - - %d attendance - %d attendance - Gesamt @@ -223,8 +185,8 @@ Neue prüfungen - Du hast %d neue Prüfung - Du hast %d neue Prüfungen + Du hast %d neue Prüfung erhalten + Sie haben %d neue Prüfungen erhalten %d prüfung @@ -250,13 +212,16 @@ Thema Inhalt Nachricht erfolgreich gesendet - Nachricht nicht vorhanden + Nachricht existiert nicht Sie müssen mindestens 1 Empfänger auswählen. Der Inhalt der Nachricht muss mindestens 3 Zeichen lang sein. Nur ungelesen Nur mit Anhängen Lesen: %s - Lesen von: %1$d von %2$d Personen + + Lesen von: %1$d von %2$d Personen + Lesen von: %1$d von %2$d Personen + %d nachricht %d nachrichten @@ -316,9 +281,6 @@ Keine Informationen über Hausaufgaben Gemacht Unvollständig - Add homework - Homework added successfully - Homework deleted successfully Anhänge Neue hausaufgaben @@ -382,8 +344,6 @@ Sie haben %1$d neue konferenz Sie haben %1$d neue konferenzen - Teilnahme an einem Meeting - Agenda Schulankündigungen Keine schulankündigungen @@ -472,7 +432,6 @@ Lektionen (Morgen) - (Today and tomorrow) Gleich: Bald: Erstens: @@ -553,11 +512,6 @@ Ja Nein Speichern - Titel - Add - Kopiert - lösen - Ändern Keine Lektionen Thema wählen @@ -565,13 +519,13 @@ Dunkel Systemthema - App + Aussehen & Verhalten Standard Ansicht - Berechnete Durchschnittsoptionen + Berechnung des Jahresenddurchschnitts Mittelwertberechnung durch App erzwingen Anwesendheit zeigen Thema - Grades expanding + Noten erweitern Aktuelle Lektion markieren Gruppen neben Schulfächen anzeigen Liste der Diagramme in Klassenbewertungen anzeigen @@ -580,22 +534,14 @@ Schulfachen sortieren Sprache Benachrichtigungen - Other Benachrichtigungen anzeigen Benachrichtigungen über bevorstehende Lektionen anzeigen - Festlegen einer Benachrichtigung über die bevorstehende Lektion dauerhaft - Deaktivieren wenn die Benachrichtigung nicht in deiner Uhr/Band angezeigt wird Systembenachrichtigungseinstellungen öffnen Synchronisierungs- und Benachrichtigungsprobleme reparieren Ihr Gerät hat möglicherweise Probleme mit der Datensynchronisierung und Benachrichtigungen.\n\nUm diese zu reparieren, fügen Sie Wulkanowy zum Autostart hinzu und deaktivieren Sie die Batterieoptimierung in den Systemeinstellungen des Geräts. + Gehe zu den Einstellungen Debug-Benachrichtigungen anzeigen Synchronisierung ist deaktiviert - Offizielle App-Benachrichtigungen erfassen - Benachrichtigungen erfassen - With this feature you can gain a substitute of push notifications like in the official app. All you need to do is allow Wulkanowy to receive all notifications in your system settings.\n\nHow it works?\nWhen you get a notification in Dziennik VULCAN, Wulkanowy will be notified (that\'s what these extra permissions are for) and will trigger a sync so that can send its own notification.\n\nFOR ADVANCED USERS ONLY - Upcoming lesson notifications - You must allow the Wulkanowy app to set alarms and reminders in your system settings to use this feature. - Go to settings Synchronisierung Automatische Aktualisierung An Feiertagen suspendiert @@ -610,26 +556,16 @@ Wert des Minus Antwort mit Nachrichtenhistorie Arithmetisches Mittel anzeigen, wenn keine Gewichte angegeben sind - Support - Watch single ad to support project - Consent to data processing - To view an advertisement you must agree to the data processing terms of our Privacy Policy - Agree - Privacy policy - Ad is loading - Thank you for your support, come back later for more ads Erweitert Aussehen & Verhalten Benachrichtigungen Synchronisierung - Advertisements Noten Dashboard Sichtbarkeit der Kacheln Schulbesuch - Stundenplan + Zeitplan Noten - Calculated average Nachrichten Aussehen & Verhalten Sprachen, Themen, Schulfachen sortieren @@ -639,8 +575,7 @@ Automatisches Update, Synchronisierungsintervall Plus und Minus Werte, Durchschnittsberechnung Erweitert - App version, contributors, social portals - Displaying advertisements, project support + App-Version, Mitarbeiter, soziale Portale, Lizenzen Neue Noten Neue Hausaufgaben @@ -653,8 +588,6 @@ Push-Benachrichtigungen Bevorstehende Lektionen Debuggen - Timetable change - New attendance Schwarz Rot @@ -662,6 +595,10 @@ Grün Violett Keine Farbe + + Kopiert + lösen + Ändern Download der Updates wurde gestartet… Ein Update wurde gerade heruntergeladen. @@ -678,5 +615,4 @@ Ein unerwarteter Fehler ist aufgetreten Funktion, die von Ihrer Schule deaktiviert wurde Feature in diesem Modus nicht verfügbar - This field is required diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index 881d5bd4f..5eca4680e 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -8,7 +8,6 @@ @color/colorErrorLight @color/colorDividerInverse @color/colorSwipeRefreshDark - @color/dashboard_message_medium_light ?colorSurface ?android:textColorPrimary @color/colorNavigationBarLight diff --git a/app/src/main/res/values-pl/preferences_values.xml b/app/src/main/res/values-pl/preferences_values.xml index c823e9608..12cfda8c9 100644 --- a/app/src/main/res/values-pl/preferences_values.xml +++ b/app/src/main/res/values-pl/preferences_values.xml @@ -40,14 +40,9 @@ Wulkanowy Kolory ocen w dzienniku - - Do 1 na raz - Zawsze rozwinięte - Nieograniczone rozwijanie - - Średnia ocen tylko z wybranego semestru - Średnia ze średnich z obu semestrów + Średnia ocen z drugiego semestru + Średnia średnich z obu semestrów Średnia wszystkich ocen z całego roku diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 8de1be637..14f23cd29 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -17,7 +17,6 @@ Licencje Wiadomości Nowa wiadomość - Nowe zadanie domowe Uwagi i osiągnięcia Zadania domowe Menadżer kont @@ -25,15 +24,14 @@ Szczegóły konta Informacje o uczniu Start - Centrum powiadomień Semestr %1$d, %2$d/%3$d Zaloguj się za pomocą konta ucznia lub rodzica - Podaj symbol ze strony dziennika dla konta: <b>%1$s</b> + Podaj symbol ze strony dziennika Nazwa użytkownika - Adres e-mail - Login, PESEL lub adres e-mail + Email + Login, PESEL lub e-mail Hasło Odmiana dziennika UONET+ Mobilne API @@ -44,16 +42,16 @@ Symbol Zaloguj To hasło jest za krótkie - Dane logowania są niepoprawne - %1$s. Upewnij się, że wybrano poprawną odmianę dziennika UONET+ poniżej + Dane logowania są niepoprawne. Upewnij się, że została wybrana odpowiednia odmiana dziennika UONET+ w polu poniżej Nieprawidłowy PIN Nieprawidłowy token Token stracił ważność - Nieprawidłowy adres e-mail - Użyj loginu zamiast adresu e-mail - Użyj loginu lub adresu e-mail w @%1$s - Nieprawidłowy symbol + Niepoprawny adres email + Użyj przydzielonego loginu zamiast emaila + Użyj przypisanego loginu lub adresu e-mail w @%1$s + Niepoprawny symbol Nie znaleziono ucznia. Sprawdź poprawność symbolu i wybranej odmiany dziennika UONET+ + To pole jest wymagane Wybrany uczeń jest już zalogowany Symbol znajdziesz na stronie dziennika w Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUpewnij się, że w polu Dziennik UONET+ na poprzednim ekranie została ustawiona odpowiednia odmiana dziennika.\n\nWulkanowy na chwilę obecną nie wykrywa uczniów przedszkolnych (z zerówki) Wybierz uczniów do zalogowania w aplikacji @@ -63,9 +61,9 @@ Połączenie najlepszych cech dwóch pozostałych trybów. Działa szybciej niż scraper i zapewnia funkcje niedostępne w trybie Mobilne API. Jest w fazie eksperymentalnej Polityka prywatności Problemy z logowaniem? Napisz do nas! - E-mail + Email Discord - Wyślij wiadomość e-mail + Wyślij email Upewnij się, że została wybrana odpowiednia odmiana dziennika UONET+! Nie pamiętam hasła Przywróć swoje konto @@ -92,10 +90,6 @@ Ocena końcowa Przewidywana ocena Obliczona średnia - Jak działa obliczona średnia? - Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\nŚrednia ocen tylko z wybranego semestru:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia ze średnich z obu semestrów:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semetrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia wszystkich ocen z całego roku:\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej z zsumowanych średnich - Jak działa końcowa średnia? - Średnią końcową jest średnia arytmetyczna obliczona na podstawie wszystkich obecnie dostępnych ocen końcowych w danym semestrze.\n\nSchemat obliczeń składa się z następujących kroków:\n1. Sumowanie końcowych ocen wpisanych przez nauczycieli\n2. Dzielenie przez liczbę przedmiotów, z których oceny zostały już wystawione Końcowa średnia z %1$d na %2$d przedmiotów Podsumowanie @@ -165,34 +159,6 @@ Teraz: %s Za chwilę: %s Później: %s - %1$s lekcja %2$d - %3$s - Zmiana sali z %1$s na %2$s - Zmiana nauczyciela z %1$s na %2$s - Zmiana przedmiotu z %1$s na %2$s - - Zmiana planu lekcji - Zmiany planu lekcji - Zmiany planu lekcji - Zmiany planu lekcji - - - %1$s - %2$d zmiana planu lekcji - %1$s - %2$d zmiany planu lekcji - %1$s - %2$d zmian planu lekcji - %1$s - %2$d zmian planu lekcji - - - %1$d zmiana planu lekcji - %1$d zmiany planu lekcji - %1$d zmian planu lekcji - %1$d zmian planu lekcji - - - %d zmiana - %d zmiany - %d zmian - %d zmian - Lekcje zrealizowane Zobacz lekcje zrealizowane @@ -222,24 +188,6 @@ Prośba o usprawiedliwienie została pomyślnie wysłana! Musisz wybrać co najmniej jedną nieobecność! Usprawiedliw - - Nowa frekwencja - Nowe frekwencje - Nowe frekwencje - Nowe frekwencje - - - %1$d nowa frekwencja - %1$d nowe frekwencje - %1$d nowych frekwencji - %1$d nowych frekwencji - - - %d frekwencja - %d frekwencje - %d frekwencji - %d frekwencji - Razem @@ -290,7 +238,12 @@ Tylko nieprzeczytane Tylko z załącznikami Przeczytana: %s - Przeczytana przez: %1$d z %2$d osób + + Przeczytana przez: %1$d z %2$d osób + Przeczytana przez: %1$d z %2$d osób + Przeczytana przez: %1$d z %2$d osób + Przeczytana przez: %1$d z %2$d osób + %d wiadomość %d wiadomości @@ -374,9 +327,6 @@ Brak zadań domowych Wykonane Niewykonane - Dodaj zadanie domowe - Zadanie domowe pomyślnie dodane - Zadanie domowe pomyślnie usunięte Załączniki Nowe zadanie domowe @@ -452,8 +402,6 @@ Masz %1$d nowych zebrań Masz %1$d nowych zebrań - Obecność na zebraniu - Agenda Ogłoszenia szkolne Brak ogłoszeń szkolnych @@ -526,7 +474,7 @@ Imiona matki i ojca Telefon Telefon komórkowy - Adres e-mail + E-mail Adres zamieszkania Adres zameldowania Adres korespondencyjny @@ -548,7 +496,6 @@ Lekcje (Jutro) - (Dzisiaj i jutro) Za chwilę: Wkrótce: Pierwsza: @@ -569,10 +516,10 @@ Następnie: Później: - jeszcze %1$d lekcja - jeszcze %1$d lekcje - jeszcze %1$d lekcji - jeszcze %1$d lekcji + jeszcze %1$d dodatkowa lekcja + jeszcze %1$d dodatkowe lekcje + jeszcze %1$d dodatkowych lekcji + jeszcze %1$d dodatkowych lekcji do %1$s Brak nadchodzących lekcji @@ -581,10 +528,10 @@ Brak prac domowych do wykonania Wystąpił błąd podczas ładowania zadań domowych - Jeszcze %1$d zadanie domowe - Jeszcze %1$d zadania domowe - Jeszcze %1$d zadań domowych - Jeszcze %1$d zadań domowych + Jeszcze %1$d dodatkowe zadanie domowe + Jeszcze %1$d dodatkowe zadania domowe + Jeszcze %1$d dodatkowych zadań domowych + Jeszcze %1$d dodatkowych zadań domowych do %1$s Ostatnie oceny @@ -594,19 +541,19 @@ Brak aktualnych ogłoszeń Wystąpił błąd podczas ładowania ogłoszeń - Jeszcze %1$d ogłoszenie - Jeszcze %1$d ogłoszenia - Jeszcze %1$d ogłoszeń - Jeszcze %1$d ogłoszeń + Jeszcze %1$d dodatkowe ogłoszenie + Jeszcze %1$d dodatkowe ogłoszenia + Jeszcze %1$d dodatkowych ogłoszeń + Jeszcze %1$d dodatkowych ogłoszeń Sprawdziany Brak nadchodzących sprawdzianów Wystąpił błąd podczas ładowania sprawdzianów - Jeszcze %1$d sprawdzian - Jeszcze %1$d sprawdziany - Jeszcze %1$d sprawdzianów - Jeszcze %1$d sprawdzianów + Jeszcze %1$d dodatkowy sprawdzian + Jeszcze %1$d dodatkowe sprawdziany + Jeszcze %1$d dodatkowych sprawdzianów + Jeszcze %1$d dodatkowych sprawdzianów Zebrania Brak nadchodzących zebrań @@ -615,7 +562,7 @@ Jeszcze %1$d dodatkowe zebranie Jeszcze %1$d dodatkowe zebrania Jeszcze %1$d dodatkowych zebrań - Jeszcze %1$d zebrań + Jeszcze %1$d dodatkowych zebrań Wystąpił błąd podczas ładowania danych Brak @@ -643,11 +590,6 @@ Tak Nie Zapisz - Tytuł - Dodaj - Skopiowano - Cofnij - Zmień Brak lekcji Wybierz motyw @@ -655,13 +597,13 @@ Ciemny Motyw systemu - Aplikacja + Wygląd i zachowanie aplikacji Domyślny widok - Opcje obliczonej średniej + Obliczanie średniej końcoworocznej Wymuś obliczanie średniej przez aplikację Pokazuj obecność Motyw - Rozwijanie ocen + Rozwiń oceny Oznaczaj bieżącą lekcję Pokazuj grupę obok przedmiotu Pokazuj listę wykresów w ocenach klasy @@ -670,22 +612,14 @@ Sortowanie przedmiotów Język Powiadomienia - Inne Pokazuj powiadomienia Pokazuj powiadomienia o nadchodzących lekcjach - Ustaw powiadomienie o nadchodzącej lekcji jako trwałe - Wyłącz, gdy powiadomienie nie jest widoczne na zegarku/opasce Otwórz systemowe ustawienia powiadomień Napraw problemy z synchronizacją i powiadomieniami Na twoim urządzeniu mogą występować problemy z synchronizacją danych i powiadomieniami.\n\nBy je naprawić, dodaj Wulkanowego do autostartu i wyłącz optymalizację/oszczędzanie baterii w ustawieniach systemowych telefonu. + Przejdź do ustawień Pokazuj powiadomienia debugowania Synchronizacja jest wyłączona - Przechwytywanie powiadomień oficjalnej aplikacji - Przechwytywanie powiadomień - Dzięki tej funkcji możesz uzyskać namiastkę powiadomień push, takich jak w oficjalnej aplikacji. Wszystko, co musisz zrobić, to zezwolić Wulkanowemu na odczytywanie wszystkich powiadomień w ustawieniach systemowych.\n\nJak to działa?\nKiedy otrzymasz powiadomienie w Dzienniczku VULCAN, Wulkanowy zostanie o tym powiadomiony (do tego jest to dodatkowe uprawnienie) i uruchomi synchronizację, aby mógł wysłać własne powiadomienie.\n\nWYŁĄCZNIE DLA ZAAWANSOWANYCH UŻYTKOWNIKÓW - Powiadomienia o nadchodzących lekcjach - Musisz pozwolić Wulkanowemu na tworzenie alarmów i przypomnień w ustawieniach Twojego systemu, aby użyć tej funkcji. - Przejdź do ustawień Synchronizacja Automatyczna aktualizacja Zawieszona na wakacjach @@ -700,26 +634,16 @@ Wartość minusa Odpowiadaj z historią wiadomości Licz średnią arytmetyczną, gdy żadna ocena nie ma wagi - Wsparcie - Obejrzyj pojedynczą reklamę, aby wesprzeć projekt - Zgoda na przetwarzanie danych - Aby obejrzeć reklamę, musisz zaakceptować warunki przetwarzania danych zawarte w naszej Polityce Prywatności - Akceptuję - Polityka prywatności - Ładowanie reklamy - Dziękujemy za wsparcie, wróć później po więcej reklam Zaawansowane Wygląd i zachowanie Powiadomienia Synchronizacja - Reklamy Oceny Start Widoczność kafelków Frekwencja Plan lekcji Oceny - Obliczona średnia Wiadomości Wygląd i zachowanie Języki, motywy, sortowanie przedmiotów @@ -729,8 +653,7 @@ Automatyczna aktualizacja, interwał synchronizacji Wartości plusa i minusa, obliczanie średniej Zaawansowane - Wersja aplikacji, twórcy, media społecznościowe - Wyświetlanie reklam, wsparcie projektu + Wersja aplikacji, twórcy, media społecznościowe, licencje Nowe oceny Nowe zadania domowe @@ -743,8 +666,6 @@ Powiadomienia push Nadchodzące lekcje Debugowanie - Zmiany planu lekcji - Nowe frekwencje Czarny Czerwony @@ -752,6 +673,10 @@ Zielony Fioletowy Brak koloru + + Skopiowano + Cofnij + Zmień Rozpoczęto pobieranie aktualizacji… Aktualizacja została pobrana. @@ -768,5 +693,4 @@ Wystąpił nieoczekiwany błąd Funkcja wyłączona przez szkołę Funkcja niedostępna. Zaloguj się w trybie innym niż Mobilne API - To pole jest wymagane diff --git a/app/src/main/res/values-ru/preferences_values.xml b/app/src/main/res/values-ru/preferences_values.xml index cfdaa9577..7c4d14df6 100644 --- a/app/src/main/res/values-ru/preferences_values.xml +++ b/app/src/main/res/values-ru/preferences_values.xml @@ -40,14 +40,9 @@ Wulkanowy Цвета оценок в дневнике - - До 1 за раз - Всегда развернуто - Неограниченные расширения - - Средние оценки только с выбранного семестра - Средние значения для обоих семестров + Средняя оценка со 2 семестра + Средняя оценка с двух семестров Средняя оценок со всего года diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index aff6b176a..09b8a146d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -17,7 +17,6 @@ Лицензии Сообщения Новое сообщение - Новая домашняя работа Предупреждения и свершения Домашние задания Менеджер аккаунтов @@ -25,12 +24,11 @@ Данные аккаунта Информация о студенте Панель - Центр уведомлений %1$d семестр, %2$d/%3$d Авторизируйтесь при помощи аккаунта ученика или родителя - Введите символ со страницы регистрации: <b>%1$s</b> + Введите символ со страницы регистрации Имя пользователя Электронная почта Логин, PESEL или электронная почта @@ -44,8 +42,7 @@ Symbol Войти Слишком короткий пароль - Данные для входа указаны неверно - %1$s. Убедитесь, что ниже выбран правильный UONET+ вариант регистра + Данные для входа неверны. Убедитесь, что в поле ниже выбран правильный вариант регистра UONET+ Неправильный PIN Неверный token Token просрочен @@ -54,6 +51,7 @@ Использовать назначенный логин или email в @%1$s Неправильный символ Студент не найден. Подтвердите символ и выбранный вариант регистра UONET+ + Это обязательное поле Данный ученик уже авторизован Этот символ можно найти на странице регистрации в  Ученик →  Телефонный доступ →   Зарегистрируйте мобильное устройство.\n\nУбедитесь, что вы установили соответствующий вариант регистра в поле Разновидностью бревна UONET+ на предыдущем экране. Вулкановый на данный момент не обнаруживает дошкольников Выберите учеников для авторизации в приложении @@ -92,10 +90,6 @@ Итоговая оценка Ожидаемая оценка Рассчитанная средняя оценка - Как рассчитывается средняя работа? - Расчетное среднее - это среднее арифметическое, рассчитанное на основе средних значений испытуемых. Это позволяет узнать приблизительное итоговое среднее значение. Он рассчитывается способом, выбранным пользователем в настройках приложения. Рекомендуется выбрать подходящий вариант. Это потому, что расчет средних показателей школы отличается. Кроме того, если ваша школа сообщает среднее значение по предметам на странице Vulcan, приложение загружает их и не вычисляет эти средние значения. Это можно изменить, принудительно вычисляя среднее значение в настройках приложения.\n\nСреднее значение только за выбранный семестр :\n1. Вычисление средневзвешенного значения по каждому предмету за семестр\n2.Добавление вычисленных средних\n3. Вычисление среднего арифметического суммарных средних\n\nСреднее из средних значений за оба семестра:\n1.Расчет средневзвешенного значения для каждого предмета в семестрах 1 и 2\n2. Вычисление среднего арифметического рассчитанных средних значений для семестров 1 и 2 по каждому предмету.\n3. Добавление вычисленных средних\n4. Расчет среднего арифметического суммированных средних\n\nСреднее значение оценок за весь год: \n1. Расчет средневзвешенного значения за год по каждому предмету. Итоговое среднее значение за 1 семестр не имеет значения.\n3. Добавление вычисленных средних\n4. Расчет среднего арифметического - Как работает окончательный средний показатель? - Среднее арифметическое - это среднее арифметическое, рассчитанное по всем имеющимся на данный момент итоговым классам данного семестра.\n\nСхема расчета состоит из следующих шагов:\n1. Суммирование итоговых классов преподавателей\n2. Деление по количеству уже оцененных предметов Итоговая средняя оценка от %1$d из %2$d субъектов Итоги @@ -165,34 +159,6 @@ Сейчас: %s Следующий: %s Позже: %s - %1$s урок %2$d - %3$s - Изменить комнату с %1$s на %2$s - Изменить учителя с %1$s на %2$s - Изменить тему с %1$s на %2$s - - Изменение расписания - Изменение расписания - Изменение расписания - Изменение расписания - - - %1$s - %2$d изменений в расписании - %1$s - %2$d изменений в расписании - %1$s - %2$d изменений в расписании - %1$s - %2$d изменений в расписании - - - %1$d - изменений в расписании - %1$d изменение в расписании - %1$d изменение в расписании - %1$d изменений в расписании - - - %d изменение - %d изменение - %d изменение - %d изменений - Проведённые уроки Просмотреть проведённые уроки @@ -222,24 +188,6 @@ Запрос на освобождение оправдания успешно отправлен! Выберите хотя-бы одно отсутствие Изменить статус - - Новое посещение - Новое посещение - Новое посещение - Новое посещение - - - %1$d новое посещения - %1$d новое посещение - %1$d новое посещение - %1$d новое посещения - - - %d посещаемость - %d посещаемость - %d посещаемость - %d посещаемость - Общая @@ -290,7 +238,12 @@ Только непрочитанные Только с вложениями Чтение: %s - Прочитано: %1$d из %2$d человек + + Прочитано: %1$d из %2$d человек + Прочитано: %1$d из %2$d человек + Прочитано: %1$d из %2$d человек + Прочитано: %1$d из %2$d человек + %d сообщение %d сообщения @@ -374,9 +327,6 @@ Нет домашних заданий Отметить как выполненное Отметить как невыполненное - Добавить домашнюю работу - Домашняя работа успешно добавлена - Домашняя работа успешно удалена Вложения Новая домашняя работа @@ -452,8 +402,6 @@ У вас %1$d новая конференция У вас %1$d новых конференций - Присутствует на конференции - Повестка дня Объявления школ Нет объявлений о школе @@ -548,7 +496,6 @@ Уроки (Завтра) - (Сегодня и завтра) Сейчас: Скоро: Первый: @@ -643,11 +590,6 @@ Да Нет Сохранить - Тема - Добавить - Скопировано - Отменить - Изменить Нет уроков Выбрать тему @@ -655,13 +597,13 @@ Тёмная Тема системы - Приложение + Внешний вид приложения & поведение Окно по умолчанию - Рассчитанные средние параметры + Способ определения средней годовой оценки Принудительно высчитать среднюю оценку через приложение Показать присутствие Тема - Расширяется оценка + Разворачивать оценки Отметить текущий урок Показать группы рядом с темами Показывать диаграммы в оценках класса @@ -670,22 +612,14 @@ Сортировка уроков Язык Уведомления - Прочее Показывать уведомления Показывать уведомления о будущих уроках - Сделать уведомления о предстоящем уроке постоянным - Выключить, когда уведомление не отображается в чата/полосе Открыть настройки уведомлений системы Исправить проблемы с синхронизацией и уведомлениями На вашем устройстве могут быть проблемы с синхронизацией данных и уведомлениями.\n\nЧтобы их исправить, вам необходимо добавить Wulkanowy в авто-старт и выключить оптимизацию/экономию батареи в настройках устройства. + Перейти в настройски Показывать дебаг-уведомления Синхронизация отключена - Записывать официальные уведомления - Показывать push-уведомления - С помощью этой функции вы можете получить замену push-уведомлений, как в официальном приложении. Все, что вам нужно сделать, это разрешить Wulkanowy получать все уведомления в настройках системы.\n\nКак это работает?\nКогда вы получаете уведомление в Dziennik VULCAN, Wulkanowy будет уведомлен (это требует дополнительных прав) и запустит синхронизацию, чтобы отправить свое уведомление.\n\nТОЛЬКО ДЛЯ ПОЛЬЗОВАТЕЛЯ - Upcoming lesson notifications - You must allow the Wulkanowy app to set alarms and reminders in your system settings to use this feature. - Go to settings Синхронизация Автоматическая синхронизация Приостановить синхронизации во время каникул @@ -700,26 +634,16 @@ Стоимость минуса Отвечать с историей сообщений Показывать среднее арифметическое при отсутствии весов - Поддержка - Смотреть одиночную рекламу для поддержки проекта - Согласие на обработку данных - Для просмотра рекламы вы должны согласиться с условиями обработки данных нашей Политики конфиденциальности - Согласен - Политика конфиденциальности - Объявление загружается - Thank you for your support, come back later for more ads Расширенные Внешний вид & Поведение Уведомления Синхронизация - Реклама Оценки Панель Видимость плиток Посещаемость Расписание Оценки - Расчетное среднее Сообщения Внешний вид & Поведение Языки, темы, темы сортировки темы @@ -729,8 +653,7 @@ Автоматическое обновление, интервал синхронизации Значения плюс и минус, средний расчет Расширенные - Версия приложения, участники, социальные порталы - Отображение объявлений, поддержка проекта + Версия приложения, участники, социальные порталы, лицензии Новые оценки Новая домашняя работа @@ -743,8 +666,6 @@ Показывать push-уведомления Будущие уроки Дебаг - Изменение расписания - Новое посещение Чёрный Красный @@ -752,6 +673,10 @@ Зелёный Фиолетовый Нет цвета + + Скопировано + Отменить + Изменить Загрузка обновлений началась… Только что было скачано обновление. @@ -768,5 +693,4 @@ Произошла неожиданная ошибка Функция была выключена школой Функция не доступна в этом режиме - Это поле является обязательным diff --git a/app/src/main/res/values-sk/preferences_values.xml b/app/src/main/res/values-sk/preferences_values.xml index e64f5606a..108af555d 100644 --- a/app/src/main/res/values-sk/preferences_values.xml +++ b/app/src/main/res/values-sk/preferences_values.xml @@ -40,20 +40,15 @@ Wulkanowy Farby známok v denníku - - Až 1 naraz - Vždy rozbalené - Neobmedzené rozbalené - - Priemer známok iba z vybraného semestra - Priemer z priemerov z oboch semestrov + Priemer známok až od druhého semestra + Priemer známok z oboch semestrov Priemer známok z celého roka Šťastné číslo Neprečítané správy - Frekvencia + Dochádzka Lekcie Známky Domáce úlohy diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 2070e53a6..ad078e33f 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -4,7 +4,7 @@ Prihlásenie Wulkanowy Známky - Frekvencia + Dochádzka Skúšky Plán lekcie Nastavenia @@ -12,12 +12,11 @@ O aplikácii Prehliadač protokolov Ladenie - Ladenie upozornení - Tvorcovia + Ladenie oznámení + Prispievatelia Licencie Správy Nová správa - Nový domáci úloh Poznámky a úspechy Domáce úlohy Manažér účtov @@ -25,12 +24,11 @@ Podrobnosti účtu Informácie o žiakovi Domov - Centrum upozornení Semester %1$d, %2$d/%3$d Prihláste sa pomocou študentského alebo rodičovského konta - Zadajte symbol zo stránky denníka: <b>%1$s</b> + Zadajte symbol zo stránky denníka Užívateľské meno Email Prihlásenie, číslo PESEL alebo e-mail @@ -44,8 +42,7 @@ Symbol Prihlásiť Toto heslo je príliš krátke - Prihlasovacie údaje sú nesprávne - %1$s. Skontrolujte, či je nižšie vybratá správna variácie denníka UONET+ + Prihlasovacie údaje sú nesprávne. Uistite sa, že je v poli nižšie vybraná správna variácie denníka UONET+ Neplatný PIN Neplatný token Platnosť tokenu vypršala @@ -54,14 +51,15 @@ Použite priradené prihlasovacie alebo e-mail v @%1$s Neplatný symbol Žiak nebol nájdený. Skontrolujte správnosť symbolu a vybrané varianty denníka UONET+ + Toto pole je povinné Vybraný žiak už je prihlásený Symbol nájdete na stránke denníka v  Uczeń→ Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUistite sa, že ste na predchádzajúcu obrazovke nastaviť správny variant denníka do poľa Variácie denníka UONET+. Wulkanowy v túto chvíľu nezistí predškolské żaków Vyberte žiakov, ktorí sa majú do aplikácie prihlásiť Iné možnosti - V tomto režime nefungujú nasledovné: šťastné číslo, štatistiky triedy, zhrnutie frekvencií, ospravedlnenie neprítomnosti, dokončené lekcie, informácie o škole a prezeranie zoznamu registrovaných zariadení + V tomto režime nefungujú nasledovné: šťastné číslo, štatistiky triedy, zhrnutie dochádzky, ospravedlnenie neprítomnosti, dokončené lekcie, informácie o škole a prezeranie zoznamu registrovaných zariadení Tento režim zobrazuje rovnaké dáta, ktoré sa zobrazujú na webových stránkach denníka Kombinácia najlepších vlastností ostatných dvoch režimov. Funguje rýchlejšie ako scraper a poskytuje funkcie, ktoré nie sú k dispozícii v režime Mobilne API. Je to v experimentálnej fáze - Ochrana osobných údajov + Zásady ochrany osobných údajov Problémy s prihlásením? Napíšte nám! Email Discord @@ -92,10 +90,6 @@ Konečná známka Predpokladaná známka Vypočítaný priemer - Ako funguje vypočítaný priemer? - Vypočítaný priemer je aritmetický priemer vypočítaný z priemerov predmetov. Umožňuje vám to poznať približný konečný priemer. Vypočítava sa spôsobom zvoleným užívateľom v nastaveniach aplikácii. Odporúča sa vybrať príslušnú možnosť. Dôvodom je rozdielny výpočet školských priemerov. Ak vaša škola navyše uvádza priemer predmetov na stránke denníka Vulcan, aplikácia si ich stiahne a tieto priemery nepočíta. To možno zmeniť vynútením výpočtu priemeru v nastavení aplikácii.\n\nPriemer známok iba z vybraného semestra:\n1. Výpočet váženého priemeru pre každý predmet v danom semestri\n2. Sčítanie vypočítaných priemerov\n3. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer priemerov z oboch semestrov:\n1. Výpočet váženého priemeru pre každý predmet v semestri 1 a 2\n2. Výpočet aritmetického priemeru vypočítaných priemerov za semestre 1 a 2 pre každý predmet.\n3. Sčítanie vypočítaných priemerov\n4. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer známok z celého roka:\n1. Výpočet váženého priemeru za rok pre každý predmet. Konečný priemer v 1. semestri je nepodstatný.\n3. Sčítanie vypočítaných priemerov\n4. Výpočet aritmetického priemeru součtených priemerov - Ako funguje konečný priemer? - Konečný priemer je aritmetický priemer vypočítaný zo všetkých aktuálne dostupných konečných známok v danom semestri.\n\nSchéma výpočtu sa skladá z nasledujúcich krokov:\n1. Sčítanie konečných známok zadaných učiteľmi\n2. Delené počtom predmetov, pre ktoré už boli vydané známky Konečný priemer z %1$d z %2$d predmetov Zhrnutie @@ -165,34 +159,6 @@ Teraz: %s Za chvíľu: %s Neskôr: %s - %1$s lekcia %2$d - %3$s - Zmena učebne z %1$s na %2$s - Zmena učiteľa z %1$s na %2$s - Zmena predmetu z %1$s na %2$s - - Zmena plánu lekcií - Zmeny plánu lekcií - Zmeny plánu lekcií - Zmeny plánu lekcií - - - %1$s - %2$d zmena plánu lekcií - %1$s - %2$d zmeny plánu lekcií - %1$s - %2$d zmien plánu lekcií - %1$s - %2$d zmien plánu lekcií - - - %1$d zmena plánu lekcií - %1$d zmeny plánu lekcií - %1$d zmien plánu lekcií - %1$d zmien plánu lekcií - - - %d zmena - %d zmeny - %d zmien - %d zmien - Dokončené lekcie Zobraziť dokončené lekcie @@ -201,11 +167,11 @@ Neprítomnosť Zdroje - Ďalšie lekcie + Ďalší lekcie Zobraziť ďalšie lekcie Žiadne informácie o ďalších lekciách - Zhrnutie frekvencií + Zhrnutie dochádzky Neprítomnosť zo školských dôvodov Ospravedlnená neprítomnosť Neospravedlnená neprítomnosť @@ -222,24 +188,6 @@ Žiadosť o ospravedlnenie neprítomnosti bola úspešne odoslaná! Musíte vybrať aspoň jednu neprítomnosť! Ospravedlniť - - Nová frekvencia - Nové frekvencie - Nové frekvencie - Nové frekvencie - - - %1$d nová frekvencia - %1$d nové frekvencie - %1$d nových frekvencií - %1$d nových frekvencií - - - %d frekvencia - %d frekvencie - %d frekvencií - %d frekvencií - Spoločne @@ -290,7 +238,12 @@ Iba neprečítané Iba s prílohami Prečítaná: %s - Prečítaná cez: %1$d z %2$d osôb + + Prečítaná cez: %1$d z %2$d osôb + Prečítaná cez: %1$d z %2$d osôb + Prečítaná cez: %1$d z %2$d osôb + Prečítaná cez: %1$d z %2$d osôb + %d správa %d správy @@ -374,9 +327,6 @@ Žiadne informácie o domácich úlohách Označiť ako hotové Nevyrobené - Pridať domácu úlohu - Domáca úloha bola úspešně pridaná - Domáca úloha bola úspešně odstránená Prílohy Nový domáci úloh @@ -452,8 +402,6 @@ Máte %1$d nových stretnutí Máte %1$d nových stretnutí - Prítomnosť na stretnutí - Agenda Školské oznámenia Žiadne školské oznámenia @@ -491,7 +439,7 @@ Osobné údaje Verzia aplikácie - Tvorcovia + Prispievatelia Zoznam vývojárov Wulkanového Nahlásiť chybu Odoslať správu o chybe e-mailom @@ -499,11 +447,11 @@ Prečítajte si často kladené otázky Server Discord Pripojte sa ku komunite Wulkanového - Stránka na Facebooku + Facebooková fanpage Twitter stránka Sledujte nás na Twitteri - Dajte like našej stránke na Facebooku - Ochrana osobných údajov + Rovnako ako naše facebooková fanpage + Zásady ochrany osobných údajov Pravidlá pre zhromažďovanie osobných údajov Systémové nastavenia Otvoriť systémové nastavenia @@ -548,7 +496,6 @@ Lekcie (Zajtra) - (Dnes a zajtra) Za chvíľu: Čoskoro: Prvá: @@ -569,10 +516,10 @@ Ďalej: Neskôr: - ešte %1$d lekcia - ešte %1$d lekcie - ešte %1$d lekcií - ešte %1$d lekcií + ešte %1$d ďalší lekcia + ešte %1$d ďalšie lekcie + ešte %1$d ďalších lekcií + ešte %1$d ďalších lekcií do %1$s Žiadne nadchádzajúce lekcie @@ -581,10 +528,10 @@ Žiadne domáce úlohy do vykonaná Pri načítaní domácich úloh došlo k chybe - Ešte %1$d domáca úloha - Ešte %1$d domáce úlohy - Ešte %1$d domácich úloh - Ešte %1$d domácich úloh + Ešte %1$d ďalšia domáca úloha + Ešte %1$d ďalšie domáce úlohy + Ešte %1$d ďalších domácich úloh + Ešte %1$d ďalších domácich úloh do %1$s Posledné známky @@ -594,28 +541,28 @@ Žiadne aktuálne oznámenia Pri načítaní oznámení došlo k chybe - Ešte %1$d oznámenie - Ešte %1$d oznámenia - Ešte %1$d oznámení - Ešte %1$d oznámení + Ešte %1$d ďalšie oznámenie + Ešte %1$d ďalšie oznámenia + Ešte %1$d ďalších oznámení + Ešte %1$d ďalších oznámení Skúšky Žiadne nadchádzajúce skúšky Pri načítaní skúšok došlo k chybe - Ešte %1$d skúška - Ešte %1$d skúšky - Ešte %1$d skúšok - Ešte %1$d skúšok + Ešte %1$d ďalšia skúška + Ešte %1$d ďalšie skúšky + Ešte %1$d ďalších skúšok + Ešte %1$d ďalších skúšok Stretnutie Žiadna nadchádzajúce stretnutie Pri načítaní stretnutí došlo k chybe - Ešte %1$d stretnutie - Ešte %1$d stretnutia - Ešte %1$d stretnutí - Ešte %1$d stretnutí + Ešte %1$d ďalšie stretnutie + Ešte %1$d ďalšie stretnutia + Ešte %1$d ďalších stretnutí + Ešte %1$d ďalších stretnutí Pri načítaní dát došlo k chybe Žiadne @@ -643,11 +590,6 @@ Áno Nie Uložiť - Titul - Pridať - Skopírované - Vrátiť - Zmeniť Žiadne lekcie Vybrať motív @@ -655,13 +597,13 @@ Tmavý Motív systému - Aplikácia + Vzhľad a správanie aplikácií Predvolené zobrazenie - Možnosti vypočítaného priemeru + Výpočet koncoročního priemeru Vynútiť priemerný výpočet podľa aplikácie Zobraziť prítomnosť Motív - Rozvijanie známok + Rozbaliť známky Označiť aktuálne lekciu Zobraziť skupiny vedľa predmetov Zobraziť zoznam grafov v známkach triedy @@ -670,22 +612,14 @@ Triedenie predmetov Jazyk Upozornenia - Iné Zobraziť upozornenia Zobraziť upozornenia o nadchádzajúcej lekciu - Nastaviť upozornenia o nadchádzajúcej lekcii ako trvalé - Vypnúť, keď upozornenia nie je vo vašom hodinkách/náramku viditeľné Otvoriť systémové nastavenia upozornení Opravte problémy so synchronizáciou a upozornením Vaše zariadenie môže mať problémy so synchronizáciou dát as upozorneniami.\n\nAk ich chcete opraviť, pridajte Wulkanového do funkcie Autostart a vypnite optimalizáciu/úsporu batérie v nastavení systému telefóne. + Prejsť do nastavení Zobraziť upozornenia o ladení Synchronizácia je vypnutá - Zachytiť upozornenia oficiálnej aplikácie - Zachytiť upozornenia - S touto funkciou môžete získať náhradu push upozornení ako v oficiálnej aplikácii. Všetko, čo musíte urobiť, je povoliť Wulkanowému čítať všetky vaše upozornenia v nastaveniach systému.\n\nAko to funguje?\nKeď dostanete oznámenie v Deníčku VULCAN, Wulkanowy bude o tom informovaný (k tomu je to dodatočné povolenie) a spustí synchronizáciu, aby mohol zaslať vlastné upozornenie.\n\nLEN PRE POKROČILÝCH POUŽĺVATEĹOV - Upozornenia o nadchádzajúcej lekciu - Musíte povoliť Wulkanovému nastaviť budíky a pripomenutie v nastavení vášho systému pre použitie tejto funkcie. - Prejsť do nastavení Synchronizácia Automatická aktualizácia Pozastavený počas dovolenky @@ -700,26 +634,16 @@ Hodnota mínusu Odpovedať s históriou správ Vypočítať aritmetický priemer, ak žiadna známka nemá váhu - Podpora - Pozrite sa na jednu reklamu pre podporu projektu - Súhlas so spracovaním dát - Ak chcete sledovať reklamu, musíte súhlasiť s podmienkami spracovania údajov v našich Zásadách Ochrany Osobných Údajov - Súhlasím - Ochrana osobných údajov - Reklama sa načítava - Ďakujeme za vašu podporu, vráťte sa neskôr pre viac reklám Pokročilé Vzhľad a správanie Upozornenia Synchronizácia - Reklamy Známky Domov Viditeľnosť dlaždíc - Frekvencia + Dochádzka Plán lekcie Známky - Vypočítaný priemer Správy Vzhľad a správanie Jazyky, motívy, triedenie predmetov @@ -729,8 +653,7 @@ Automatická aktualizácia, interval aktualizácií Hodnota plusu a mínusu, výpočet priemeru Pokročilé - Verzia aplikácie, tvorcovia, sociálne portály - Zobrazovanie reklám, podpora projektu + Verzia aplikácie, prispievatelia, sociálne portály, licencie Nové známky Nové domáce úlohy @@ -743,8 +666,6 @@ Push upozornenia Nadchádzajúce lekcie Ladenie - Zmeny plánu lekcií - Nové frekvencie Čierna Červená @@ -752,6 +673,10 @@ Zelená Fialová Žiadna farba + + Skopírované + Vrátiť + Zmeniť Sťahovanie aktualizácií začalo… Aktualizácia bola stiahnutá. @@ -768,5 +693,4 @@ Vyskytla sa neočakávaná chyba Funkcia je deaktivovaná cez vašou školou Funkcia nie je k dispozícii. Prihláste sa v inom režime než Mobile API - Toto pole je povinné diff --git a/app/src/main/res/values-uk/preferences_values.xml b/app/src/main/res/values-uk/preferences_values.xml index a8c09bcf8..f6f5b984f 100644 --- a/app/src/main/res/values-uk/preferences_values.xml +++ b/app/src/main/res/values-uk/preferences_values.xml @@ -40,14 +40,9 @@ Wulkanowy Кольори оцінок в щоденнику - - Раз до 1 - Завжди розгорнутий - Необмежена кількість розширень - - Середні оцінки тільки від обраного семестру - Середнє значення для обох семестів + Середня оцінка з 2 семестру + Середнє оцінювання за обидва семестри Середнє оцінювання за весь рік diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 3d14651ea..41f1c300c 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -17,7 +17,6 @@ Ліцензії Повідомлення Нове повідомлення - Нова домашня робота Нотатки та досягнення Домашні завдання Менеджер аккаунтів @@ -25,12 +24,11 @@ Деталі облікового запису Інформація про учня Дошка - Журнал сповіщень %1$d семестр, %2$d/%3$d Авторизуйтеся за допомогою аккаунта учня або батьків - Введіть символ зі сторінки реєстру: <b>%1$s</b> + Введіть символ зі сторінки реєстру Ім\'я користувача Електронна пошта Логін, PESEL або електронна пошта @@ -44,8 +42,7 @@ Symbol Увійти Занадто короткий пароль - Вказані невірні дані - %1$s. Переконайтеся, що обрано правильну варіацію запису UONET+ + Дані для входу неправильні. Переконайтеся, що у полі нижче вказано правильний варіант реєстрації UONET+ Неправильний PIN Неправильний token Минув термін дії токену @@ -54,6 +51,7 @@ Використовуйте призначений логін або електронну адресу в @%1$s Неправильний симбвол Студента не знайдено Перевірте символ та обраний варіант реєстру UONET+ + Обов\'язкове поле Даного учня вже авторизовано Символ можна знайти на сторінці реєстру в   Учень →   Мобільний доступ →   Додайте мобільне приладдя .\n\nПереконайтесь, що ви встановили відповідний варіант реєстру в полі UONET + варіант реєстрації на попередньому екрані. На даний момент Wulkanowy не виявляє учнів дошкільних закладів Виберіть учнів для авторизації в додатку @@ -92,10 +90,6 @@ Підсумкова оцінка Очікувана оцінка Розрахована середня оцінка - Як розраховується середньо? - Розрахункове середнє - це середнє арифметичне, обчислене з середніх показників для випробуваних. Це дозволяє дізнатися приблизну кінцеву середню величину. Він розраховується способом, обраним користувачем у налаштуваннях програми. Рекомендується вибрати відповідний варіант. Це пояснюється тим, що розрахунок середніх показників за школою відрізняється. Крім того, якщо у вашій школі повідомляється середнє значення предметів на сторінці Вулкан, програма завантажує їх і не обчислює ці середні значення. Це можна змінити шляхом примусового розрахунку середнього значення в налаштуваннях програми.\n\nСередні оцінки лише за вибраний семестр :\n1. Розрахунок середньозваженого для кожного предмета в даному семестрі\n2.Додавання розрахункових середніх\n3. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення середніх показників за обидва семестри :\n1.Обчислення середньозваженого значення для кожного предмета у 1 та 2 семестрах\n2. Обчислення середнього арифметичного розрахункових середніх показників за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахункових середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого показника за рік для кожного предмета. Остаточний середній показник у 1 -му семестрі не має значення.\n3. Додавання розрахункових середніх \n4. Обчислення середнього арифметичного середніх суммованих середніх - Як працює кінцевий середній показник? - Підсумкове середнє значення - це середнє арифметичне, обчислене з усіх наявних наразі підсумкових оцінок у даному семестрі. \ N \ nСхема обчислення складається з таких кроків: \ n1. Підбиття підсумкових оцінок викладачів \ n2. Поділіть на кількість предметів, які вже оцінені Підсумкова середня оцінка з %1$d із %2$d тем Підсумок @@ -165,34 +159,6 @@ Зараз: %s Наступний: %s Пізніше: %s - %1$s урок %2$d - %3$s - Зміна місця з %1$s на %2$s - Змінити вчителя з %1$s на %2$s - Зміна теми з %1$s на %2$s - - Зміна у розкладі - Зміна у розкладі - Зміна у розкладі - Зміни у розкладі рейсу - - - %1$s - %2$d зміна в розкладі - %1$s - %2$d зміна в розкладі - %1$s - %2$d зміна в розкладі - %1$s - %2$d змін у розкладі - - - %1$d зміна в розкладі - %1$d зміна в розкладі - %1$d зміна в розкладі - %1$d змін у розкладі - - - %d зміна - %d зміна - %d зміна - %d змін - Уроки, що відбулися Показати уроки, що відбулися @@ -222,24 +188,6 @@ Запит на виправдання відсутності успішно надіслано! Оберіть хоча б одну відсутність Змінити статус - - Нова відвідуваність - Нова відвідуваність - Нова відвідуваність - Нова відвідуваність - - - %1$d новий відвідувач - %1$d новий відвідувач - %1$d новий відвідувач - %1$d відвідування - - - %d відвідування - %d відвідування - %d відвідування - %d відвідування - Загальна @@ -290,7 +238,12 @@ Лише непрочитані Тільки з вкладеннями Читання: %s - Прочитанно:%1$d через %2$d людей + + Прочитані: %1$d з %2$d осіб + Прочитані: %1$d з %2$d осіб + Прочитані: %1$d з %2$d осіб + Прочитані: %1$d з %2$d осіб + %d повідомлення %d повідомлення @@ -374,9 +327,6 @@ Брак домашніх завдань Позначити як зроблене Позначити як не зроблене - Додати домашню роботу - Домашню роботу додано успішно - Домашню роботу видалено успішно Додатки Нова домашня робота @@ -452,8 +402,6 @@ У вас є %1$d нова конференція У вас є %1$d нових конференцій - Присутність на конференції - Порядок денний Оголошення школи Жодних навчальних оголошень @@ -548,7 +496,6 @@ Уроки (Завтра) - (сьогодні та завтра) Через мить: Незабаром: Перше: @@ -643,11 +590,6 @@ Так Ні Зберегти - Титул - Додати - Скопійовано - Відмінити - Змінити Брак уроків Увібрати тему @@ -655,13 +597,13 @@ Темна Тема системи - Додатки + Поява додатка & amp; поведінки Вікно за замовчуванням - Розрахункові середні параметри + Спосіб облічування оцінки на кінець року Примусово розрахувати середню оцінку через додаток Показати присутність Тема - Розширення оцінок + Більше оцінок Позначити поточний урок Показувати групи поруч з темами Показувати діаграми в оцінках класу @@ -670,22 +612,14 @@ Сортування предметів Мова Повідомлення - Інше Показувати повідомлення Показувати повідомлення о наступних уроках - Зробити сповіщення майбутнього уроку нестійкими - Вимкнути коли сповіщення не показуються у відстежувачі/темпі Відкрити налаштування сповіщень системи Виправити помилки з синхронізацією і повідомленнями На вашому пристрої можуть бути помилки з синхронізацією і повідомленнями\n\nЩоб виправити іх, вам необхідно додати Wulkanowy в авто-старт и вимкнути оптимізацію/экономію батареї в налаштуваннях пристрою. + Перейти до налаштувань Показувати дебаг-повідомлення Синхронізація вимкнена - Захоплювати офіційні сповіщення програм - Показувати push-повідомлення - За допомогою цієї функції ви можете отримати заміну push -повідомлень, як у офіційному додатку. Все, що вам потрібно зробити, це дозволити Wulkanowy отримувати всі сповіщення у налаштуваннях вашої системи. \ N \ nЯк це працює? \ NКоли ви отримаєте сповіщення у Dziennik VULCAN, Wulkanowy отримає сповіщення (для цього призначені ці додаткові дозволи) і запустить синхронізація, яка може надсилати власне сповіщення. \ n \ n ТІЛЬКИ ДЛЯ РОЗШИРЕНИХ КОРИСТУВАЧІВ - Upcoming lesson notifications - You must allow the Wulkanowy app to set alarms and reminders in your system settings to use this feature. - Go to settings Синхронізація Автоматична синхронізація Призупинено на час канікул @@ -700,26 +634,16 @@ Вага мінуса Відповісти з історією повідомлень Показувати в середньому арифметику, якщо немає ваги - Підтримка - Відстежуйте єдину рекламу для підтримки проекту - Згода в обробці даних - Щоб переглянути рекламу, ви повинні погодитися з умовами обробки даних нашої Політики конфіденційності - Погоджуюсь - Політика конфіденційності - Реклама завантажується - Thank you for your support, come back later for more ads Додатково Вигляд & Поведінка Повідомлення Синхронізація - Реклама Оцінки Дошка Видимість плиток Відвідуваність Розклад Класи - Обчислена середня Повідомлення Вигляд & Поведінка Мови, теми, тема сортування @@ -729,8 +653,7 @@ Автоматичне оновлення, інтервал синхронізації Плюс і мінус значення, середні обчислення Додатково - Версія програми, учасники, соціальні портали - Відображається реклама, підтримка проектів + Версія програми, учасники, соціальні портали, ліцензії Нові оцінки Нова домашня робота @@ -743,8 +666,6 @@ Показувати push-повідомлення Наступні уроки Дебаг - Зміна у розкладі - Нова відвідуваність Чорний Червоний @@ -752,6 +673,10 @@ Зелений Фіолетовий Брак кольору + + Скопійовано + Відмінити + Змінити Завантаження оновлень розпочато… Щойно завантажено оновлення. @@ -768,5 +693,4 @@ Відбулася несподівана помилка Функція вимкнена школою Функція не доступна в цьому режимі - Це поле обов\'язкове для заповнення diff --git a/app/src/main/res/values-v23/styles.xml b/app/src/main/res/values-v23/styles.xml index 840f53573..574e8488d 100644 --- a/app/src/main/res/values-v23/styles.xml +++ b/app/src/main/res/values-v23/styles.xml @@ -1,8 +1,14 @@ - + + \ No newline at end of file diff --git a/app/src/main/res/values-v26/styles.xml b/app/src/main/res/values-v26/styles.xml index 3fb0a5dd8..55413c053 100644 --- a/app/src/main/res/values-v26/styles.xml +++ b/app/src/main/res/values-v26/styles.xml @@ -5,4 +5,11 @@ false @android:color/darker_gray + + diff --git a/app/src/main/res/values-v28/styles.xml b/app/src/main/res/values-v28/styles.xml index a936566f0..ee77091d3 100644 --- a/app/src/main/res/values-v28/styles.xml +++ b/app/src/main/res/values-v28/styles.xml @@ -6,4 +6,12 @@ true @android:color/white + + \ No newline at end of file diff --git a/app/src/main/res/values-v29/styles.xml b/app/src/main/res/values-v29/styles.xml index a936566f0..ee77091d3 100644 --- a/app/src/main/res/values-v29/styles.xml +++ b/app/src/main/res/values-v29/styles.xml @@ -6,4 +6,12 @@ true @android:color/white + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index d4ed6e971..49ef39ab3 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -3,5 +3,4 @@ - diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f3112b101..a68e2710f 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -12,9 +12,6 @@ #1C1C1C #0D0D0D - #FFD980 - #ffd54f - #ffd54f #ff8f00 diff --git a/app/src/main/res/values/preferences_defaults.xml b/app/src/main/res/values/preferences_defaults.xml index 7fb3d5c05..1ba9359cd 100644 --- a/app/src/main/res/values/preferences_defaults.xml +++ b/app/src/main/res/values/preferences_defaults.xml @@ -4,7 +4,7 @@ true only_one_semester false - one + false false light vulcan @@ -14,7 +14,6 @@ false true false - true false 0.33 0.33 @@ -27,7 +26,6 @@ false false 0 - false LUCKY_NUMBER MESSAGES diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml index fef062dd1..09dac7002 100644 --- a/app/src/main/res/values/preferences_keys.xml +++ b/app/src/main/res/values/preferences_keys.xml @@ -5,8 +5,7 @@ app_theme dashboard_tiles grade_color_scheme - expand_grade - expand_grade_mode + expand_grade grade_average_mode grade_average_always_calc grade_statistics_list @@ -19,7 +18,6 @@ notifications_system_settings notifications_enable notifications_upcoming_lessons_enable - notifications_upcoming_lessons_persistent notification_debug grade_modifier_plus grade_modifier_minus @@ -34,6 +32,4 @@ message_send_is_draft message_send_recipients last_sync_date - notifications_piggyback - single_ad_support diff --git a/app/src/main/res/values/preferences_values.xml b/app/src/main/res/values/preferences_values.xml index 1d777bdb6..9c1a0421a 100644 --- a/app/src/main/res/values/preferences_values.xml +++ b/app/src/main/res/values/preferences_values.xml @@ -99,20 +99,9 @@ grade_color - - Up to 1 at once - Always expanded - Unlimited expansions - - - one - always - any - - - Average of grades only from selected semester - Average of averages from both semesters + Average of grades only from the 2nd semester + Average of grades from both semesters Average of grades from the whole year diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 43720164b..cf8a07604 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -17,7 +17,6 @@ Licenses Messages New message - New homework Notes and achievements Homework Accounts manager @@ -25,7 +24,6 @@ Account details Student info Dashboard - Notifications center @@ -34,7 +32,7 @@ Sign in with the student or parent account - Enter the symbol from the register page for account: <b>%1$s</b> + Enter the symbol from the register page Username Email Login, PESEL or e-mail @@ -48,8 +46,7 @@ Symbol Sign in Password too short - Login details are incorrect - %1$s. Make sure the correct UONET+ register variation is selected below + Login details are incorrect. Make sure the correct UONET+ register variation is selected in the field below Invalid PIN Invalid token Token expired @@ -58,6 +55,7 @@ Use the assigned login or email in @%1$s Invalid symbol Student not found. Validate the symbol and the chosen variation of the UONET+ register + This field is required Selected student is already logged in The symbol can be found on the register page in Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nMake sure that you have set the appropriate register variant in the UONET+ register variant field on the previous screen. Wulkanowy does not detect pre-school students at the moment Select students to log in to the application @@ -71,7 +69,7 @@ Discord Send email Zgłoszenie: Problemy z logowaniem - Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nDodatkowe informacje: %4$s\nOstatni błąd: %5$s\n\nPełna nazwa szkoły i klasa ucznia: + Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nDodatkowe informacje: %4$s\nOstatni błąd: %5$s\n\nOpis problemu: Make sure you select the correct UONET+ register variation! I forgot my password Recover your account @@ -102,10 +100,6 @@ Final grade Predicted grade Calculated average - How does Calculated Average work? - The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n3. Adding calculated averages\n4. Calculating the arithmetic average of summed averages - How does the Final Average work? - The Final Average is the arithmetic average calculated from all currently available final grades in the given semester.\n\nThe calculation scheme consists of the following steps:\n1. Summing up the final grades given by teachers\n2. Divide by the number of subjects that have already been graded Final average from %1$d of %2$d subjects Summary @@ -163,26 +157,6 @@ Now: %s Next: %s Later: %s - %1$s lesson %2$d - %3$s - Change of room from %1$s to %2$s - Change of teacher from %1$s to %2$s - Change of subject from %1$s to %2$s - - Timetable change - Timetable changes - - - %1$s - %2$d change in timetable - %1$s - %2$d changes in timetable - - - %1$d change in timetable - %1$d changes in timetable - - - %d change - %d changes - @@ -220,18 +194,6 @@ Excuse z powodu Dzień dobry,\nProszę o usprawiedliwienie mojego dziecka w dniu %s z lekcji %s%s%s.\n\nPozdrawiam. - - New attendance - New attendance - - - %1$d new attendance - %1$d attendance - - - %d attendance - %d attendance - @@ -282,7 +244,10 @@ Only unread Only with attachments Read: %s - Read by: %1$d of %2$d people + + Read by: %1$d of %2$d people + Read by: %1$d of %2$d people + %d message %d messages @@ -348,9 +313,6 @@ No info about homework Mark as done Mark as undone - Add homework - Homework added successfully - Homework deleted successfully Attachments New homework @@ -374,12 +336,10 @@ Today\'s lucky number is: %s Show history - Lucky number history No info about lucky numbers - Mobile devices No devices @@ -428,8 +388,7 @@ You have %1$d new conference You have %1$d new conferences - Present at conference - Agenda + School announcements @@ -536,7 +495,6 @@ Lessons (Tomorrow) - (Today and tomorrow) In a moment: Soon: First: @@ -627,11 +585,6 @@ Yes No Save - Title - Add - Copied - Undo - Change @@ -643,13 +596,13 @@ - App + App appearance & behavior Default view - Calculated average options + Calculation of the end-of-year average Force average calculation by app Show presence Theme - Grades expanding + Expand grades Mark current lesson Show groups next to subjects Show chart list in class grades @@ -659,22 +612,14 @@ Language Notifications - Other Show notifications Show upcoming lesson notifications - Make upcoming lesson notification persistent - Turn off when notification is not showing in your watch/band Open system notification settings Fix synchronization & notifications issues Your device may have data synchronization issues and with notifications.\n\nTo fix them, you need to add Wulkanowy to the autostart and turn off battery optimization/saving in the phone settings. + Go to settings Show debug notifications Synchronization is disabled - Capture official app notifications - Capture notifications - With this feature you can gain a substitute of push notifications like in the official app. All you need to do is allow Wulkanowy to receive all notifications in your system settings.\n\nHow it works?\nWhen you get a notification in Dziennik VULCAN, Wulkanowy will be notified (that\'s what these extra permissions are for) and will trigger a sync so that can send its own notification.\n\nFOR ADVANCED USERS ONLY - Upcoming lesson notifications - You must allow the Wulkanowy app to set alarms and reminders in your system settings to use this feature. - Go to settings Synchronization Automatic update @@ -692,20 +637,10 @@ Reply with message history Show arithmetic average when no weights provided - Support - Watch single ad to support project - Consent to data processing - To view an advertisement you must agree to the data processing terms of our Privacy Policy - Agree - Privacy policy - Ad is loading - Thank you for your support, come back later for more ads - Advanced Appearance & Behavior Notifications Synchronization - Advertisements Grades Dashboard @@ -713,7 +648,6 @@ Attendance Timetable Grades - Calculated average Messages Appearance & Behavior @@ -724,8 +658,7 @@ Automatic update, synchronization interval Plus and minus values, average calculation Advanced - App version, contributors, social portals - Displaying advertisements, project support + App version, contributors, social portals, licenses @@ -740,8 +673,6 @@ Push notifications Upcoming lessons Debug - Timetable change - New attendance @@ -753,6 +684,12 @@ No color + + Copied + Undo + Change + + Download of updates has started… An update has just been downloaded. @@ -771,5 +708,4 @@ An unexpected error occurred Feature disabled by your school Feature not available. Login in a mode other than Mobile API - This field is required diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 7cd0f7258..628aa2974 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -11,7 +11,6 @@ @color/colorError @color/colorDivider @color/colorSwipeRefresh - @color/dashboard_message_medium_dark @android:color/darker_gray ?android:textColorPrimary @style/PreferenceThemeOverlay @@ -23,11 +22,10 @@ true - @@ -50,6 +49,8 @@ 11sp +