diff --git a/.github/workflows/deploy-store.yml b/.github/workflows/deploy-store.yml index e8237a381..3ce618ca7 100644 --- a/.github/workflows/deploy-store.yml +++ b/.github/workflows/deploy-store.yml @@ -13,10 +13,10 @@ jobs: environment: google-play steps: - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: 17 + java-version: 11 - uses: actions/cache@v3 with: path: | @@ -49,10 +49,10 @@ jobs: environment: app-gallery steps: - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: 17 + java-version: 11 - uses: actions/cache@v3 with: path: | diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml index c4f55e6af..20082590d 100644 --- a/.github/workflows/deploy-test.yml +++ b/.github/workflows/deploy-test.yml @@ -19,10 +19,10 @@ jobs: environment: app-center steps: - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: 17 + java-version: 11 - uses: actions/cache@v3 with: path: | @@ -89,10 +89,10 @@ jobs: if: github.event_name != 'pull_request_target' steps: - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: 17 + java-version: 11 - uses: actions/cache@v3 with: path: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6d50c45d5..3def08953 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,12 +2,10 @@ name: Tests on: push: - branches: - - master - - develop - - 'hotfix/**' + branches: [ master, develop ] tags: [ '*' ] pull_request: + branches: [ master, develop ] jobs: @@ -19,10 +17,10 @@ jobs: - uses: fkirc/skip-duplicate-actions@master - uses: actions/checkout@v3 - uses: gradle/wrapper-validation-action@v1 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: 17 + java-version: 11 - uses: actions/cache@v3 with: path: | @@ -45,10 +43,10 @@ jobs: - uses: fkirc/skip-duplicate-actions@master - uses: actions/checkout@v3 - uses: gradle/wrapper-validation-action@v1 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: 17 + java-version: 11 - uses: actions/cache@v3 with: path: | @@ -71,10 +69,10 @@ jobs: - uses: fkirc/skip-duplicate-actions@master - uses: actions/checkout@v3 - uses: gradle/wrapper-validation-action@v1 - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v2 with: distribution: 'zulu' - java-version: 17 + java-version: 11 - uses: actions/cache@v3 with: path: | diff --git a/.gitignore b/.gitignore index 921bd0a9a..cd5ff7146 100644 --- a/.gitignore +++ b/.gitignore @@ -119,4 +119,3 @@ Thumbs.db app/src/release/agconnect-services.json app/src/release/agconnect-credentials.json .idea/deploymentTargetDropDown.xml -.idea/kotlinc.xml diff --git a/.idea/migrations.xml b/.idea/migrations.xml deleted file mode 100644 index f8051a6f9..000000000 --- a/.idea/migrations.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/LICENSE b/LICENSE index a1fc37058..c97032f74 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2023 Wulkanowy + Copyright 2022 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 index 8171b27d6..5c1e5ea71 100644 --- a/README.cs.md +++ b/README.cs.md @@ -1,13 +1,18 @@ -Česká verze / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md) +[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/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče @@ -34,7 +39,7 @@ Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče * podpora více účtů s možností přejmenování žáků * tmavý a černý (AMOLED) motiv * offline režim -* volitelné reklamy na podporu projektu +* žádné reklamy ## Stáhnout @@ -52,7 +57,7 @@ Aktuální verzi si můžete stáhnout z Google Play, F-Droid nebo Huawei AppGal Můžete si také stáhnout [vývojovou verzi](https://wulkanowy.github.io/#download), která zahrnuje nové funkce připravované pro příští vydání -## Postaveno s pomocí +## Postaveno s * [Wulkanowy SDK](https://github.com/wulkanowy/sdk) diff --git a/README.de.md b/README.de.md index 972f66ba9..b9e1d1ec1 100644 --- a/README.de.md +++ b/README.de.md @@ -1,13 +1,14 @@ -[Česká verze](README.cs.md) / Deutsche Version / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md) +[Polska wersja README](README.md) + +[English version of README](README.en.md) # Wulkanowy -[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre Eltern @@ -21,7 +22,7 @@ Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre * Prozentsatz der Anwesenheit * Prüfungen * Stundenplan - * abgeschlossene Unterrichtsstunden + * Unterricht abgeschlossen * Nachrichten * Hausaufgaben * Anmerkungen @@ -34,7 +35,7 @@ Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre * Unterstützung für mehrere Konten mit der Möglichkeit, den Namen des Schülers zu ändern * dunkles und schwarzes (AMOLED) Thema * Offline-Modus -* optionale Werbungen, die es uns ermöglichen das Projekt zu unterstützen +* keine Werbung ## Herunterladen diff --git a/README.en.md b/README.en.md index 6e4da4637..1ac2a6721 100644 --- a/README.en.md +++ b/README.en.md @@ -1,13 +1,18 @@ -[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / English version / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md) +[Polska wersja README](README.md) + +[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/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Unofficial android VULCAN UONET+ register client for both students and their parents @@ -34,7 +39,7 @@ Unofficial android VULCAN UONET+ register client for both students and their par * support for multiple accounts with the ability to rename students * dark and black (AMOLED) theme * offline mode -* optional ads which allow to support the project +* no ads ## Download diff --git a/README.md b/README.md index f3d2e29a2..e7c7d4c5e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,18 @@ -[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / Polska wersja / [Slovenská verzia](README.sk.md) +[English version of README](README.en.md) + +[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/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica @@ -34,7 +39,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica * obsługa wielu kont wraz z możliwością zmiany nazwy ucznia * ciemny i czarny (AMOLED) motyw * tryb offline -* opcjonalne reklamy umożliwiające wsparcie projektu +* brak reklam ## Pobierz diff --git a/README.sk.md b/README.sk.md index ff0c6e3c9..2f3ba41dd 100644 --- a/README.sk.md +++ b/README.sk.md @@ -1,13 +1,18 @@ -[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / Slovenská verzia +[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/actions/workflow/status/wulkanowy/wulkanowy/test.yml?branch=develop&style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Tests/develop?style=flat-square)](https://github.com/wulkanowy/wulkanowy/actions) [![Codecov](https://img.shields.io/codecov/c/github/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://codecov.io/gh/wulkanowy/wulkanowy) [![Discord](https://img.shields.io/discord/390889354199040011.svg?style=flat-square)](https://discord.gg/vccAQBr) [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -[![Crowdin](https://badges.crowdin.net/wulkanowy2/localized.svg)](https://translate.wulkanowy.net.pl) Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov @@ -34,7 +39,7 @@ Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov * podpora viacerých účtov s možnosťou premenovania žiakov * tmavý a čierny (AMOLED) motív * offline režim -* voliteľné reklamy na podporu projektu +* žiadne reklamy ## Stiahnuť @@ -52,7 +57,7 @@ Aktuálnu verziu si môžete stiahnuť z Google Play, F-Droid alebo Huawei AppGa Môžete si tiež stiahnuť [vývojovú verziu](https://wulkanowy.github.io/#download), ktorá zahrňuje nové funkcie pripravované pre budúce vydanie -## Postavené s pomocou +## Postavené s * [Wulkanowy SDK](https://github.com/wulkanowy/sdk) diff --git a/app/build.gradle b/app/build.gradle index 180df1a6a..e8902fddd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,11 +1,8 @@ -import com.github.triplet.gradle.androidpublisher.ReleaseStatus -import ru.cian.huawei.publish.ReleaseNote - apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlinx-serialization' apply plugin: 'kotlin-parcelize' -apply plugin: 'com.google.devtools.ksp' +apply plugin: 'kotlin-kapt' apply plugin: 'dagger.hilt.android.plugin' apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.firebase.crashlytics' @@ -13,29 +10,37 @@ apply plugin: 'com.github.triplet.play' apply plugin: 'ru.cian.huawei-publish' apply plugin: 'com.mikepenz.aboutlibraries.plugin' apply plugin: 'com.huawei.agconnect' -apply plugin: 'kotlin-kapt' apply from: 'jacoco.gradle' apply from: 'sonarqube.gradle' apply from: 'hooks.gradle' android { namespace 'io.github.wulkanowy' - compileSdk 34 + compileSdkVersion 32 defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 - targetSdkVersion 34 - versionCode 143 - versionName "2.3.3" + targetSdkVersion 32 + versionCode 113 + versionName "1.7.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" + manifestPlaceholders = [ firebase_enabled: project.hasProperty("enableFirebase"), admob_project_id: "" ] + javaCompileOptions { + annotationProcessorOptions { + arguments += [ + "room.schemaLocation": "$projectDir/schemas".toString(), + "room.incremental" : "true" + ] + } + } buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null" buildConfigField "String", "DASHBOARD_TILE_AD_ID", "null" @@ -68,7 +73,6 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.release buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\"" - buildConfigField "String", "SCHOOLS_BASE_URL", '"https://schools.wulkanowy.net.pl"' } debug { minifyEnabled false @@ -78,11 +82,10 @@ android { versionNameSuffix "-dev" ext.enableCrashlytics = project.hasProperty("enableFirebase") buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\"" - buildConfigField "String", "SCHOOLS_BASE_URL", '"https://schools.wulkanowy.net.pl"' } } - flavorDimensions += "platform" + flavorDimensions "platform" productFlavors { hms { @@ -113,7 +116,6 @@ android { buildFeatures { viewBinding true - buildConfig true } bundle { @@ -122,20 +124,20 @@ android { } } - testOptions { - unitTests.includeAndroidResources = true + testOptions.unitTests { + includeAndroidResources = true // workaround HMS test errors https://github.com/robolectric/robolectric/issues/2750 - unitTests.all { jvmArgs '-noverify' } + all { jvmArgs '-noverify' } } compileOptions { coreLibraryDesugaringEnabled true - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = "17" + jvmTarget = "11" freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"] } @@ -154,16 +156,13 @@ android { kapt { correctErrorTypes true } -ksp { - arg("room.schemaLocation", "$projectDir/schemas".toString()) -} play { defaultToAppBundles = false track = 'production' - releaseStatus = ReleaseStatus.IN_PROGRESS - userFraction = 0.99d - updatePriority = 3 +// releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS +// userFraction = 0.05d + updatePriority = 5 enabled.set(false) } @@ -172,60 +171,54 @@ huaweiPublish { hmsRelease { credentialsPath = "$rootDir/app/src/release/agconnect-credentials.json" buildFormat = "aab" - deployType = "publish" - releaseNotes = [ - new ReleaseNote( - "pl-PL", - "$projectDir/src/main/play/release-notes/pl-PL/default.txt" - ) - ] + deployType = "draft" } } } ext { - work_manager = "2.9.0" - android_hilt = "1.1.0" - room = "2.6.1" - chucker = "4.0.0" - mockk = "1.13.9" - coroutines = "1.7.3" + work_manager = "2.7.1" + android_hilt = "1.0.0" + room = "2.4.3" + chucker = "3.5.2" + mockk = "1.12.7" + coroutines = "1.6.4" } dependencies { - implementation 'io.github.wulkanowy:sdk:2.3.5' + implementation "io.github.wulkanowy:sdk:1.7.4" - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' - implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines" - implementation 'androidx.core:core-ktx:1.12.0' - implementation 'androidx.core:core-splashscreen:1.0.1' - implementation "androidx.activity:activity-ktx:1.8.2" - implementation "androidx.appcompat:appcompat:1.6.1" - implementation "androidx.fragment:fragment-ktx:1.6.2" - implementation "androidx.annotation:annotation:1.7.1" + implementation "androidx.core:core-ktx:1.8.0" + implementation 'androidx.core:core-splashscreen:1.0.0' + implementation "androidx.activity:activity-ktx:1.5.1" + implementation "androidx.appcompat:appcompat:1.5.0" + implementation "androidx.fragment:fragment-ktx:1.5.2" + implementation "androidx.annotation:annotation:1.4.0" - implementation "androidx.preference:preference-ktx:1.2.1" - implementation "androidx.recyclerview:recyclerview:1.3.2" - implementation "androidx.viewpager2:viewpager2:1.1.0-beta02" + implementation "androidx.preference:preference-ktx:1.2.0" + implementation "androidx.recyclerview:recyclerview:1.2.1" + implementation "androidx.viewpager2:viewpager2:1.1.0-beta01" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0" - implementation "com.google.android.material:material:1.10.0" + implementation "com.google.android.material:material:1.6.1" implementation "com.github.wulkanowy:material-chips-input:2.3.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" - implementation 'com.github.lopspower:CircularImageView:4.3.0' + implementation 'com.github.lopspower:CircularImageView:4.2.0' - implementation "androidx.work:work-runtime:$work_manager" + implementation "androidx.work:work-runtime-ktx:$work_manager" playImplementation "androidx.work:work-gcm:$work_manager" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.2" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1" implementation "androidx.room:room-runtime:$room" implementation "androidx.room:room-ktx:$room" - ksp "androidx.room:room-compiler:$room" + kapt "androidx.room:room-compiler:$room" implementation "com.google.dagger:hilt-android:$hilt_version" kapt "com.google.dagger:hilt-android-compiler:$hilt_version" @@ -236,37 +229,32 @@ dependencies { implementation "com.github.YarikSOffice:lingver:1.3.0" implementation 'com.squareup.retrofit2:retrofit:2.9.0' - implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0" - implementation "com.squareup.okhttp3:logging-interceptor:4.12.0" + implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0" + implementation "com.squareup.okhttp3:logging-interceptor:4.10.0" implementation "com.jakewharton.timber:timber:5.0.1" - implementation 'com.github.Faierbel:slf4j-timber:2.0' - implementation 'com.github.bastienpaulfr:Treessence:1.1.2' + implementation "at.favre.lib:slf4j-timber:1.0.1" + implementation 'com.github.bastienpaulfr:Treessence:1.0.5' implementation "com.mikepenz:aboutlibraries-core:$about_libraries" - implementation 'io.coil-kt:coil:2.5.0' - implementation "io.github.wulkanowy:AppKillerManager:3.0.1" + implementation "io.coil-kt:coil:2.2.0" + implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation 'me.xdrop:fuzzywuzzy:1.4.0' - implementation 'com.fredporciuncula:flow-preferences:1.9.1' - implementation 'org.apache.commons:commons-text:1.11.0' + implementation 'com.fredporciuncula:flow-preferences:1.8.0' - playImplementation platform('com.google.firebase:firebase-bom:32.7.0') - playImplementation 'com.google.firebase:firebase-analytics' - playImplementation 'com.google.firebase:firebase-messaging' + playImplementation platform('com.google.firebase:firebase-bom:30.3.2') + playImplementation 'com.google.firebase:firebase-analytics-ktx' + playImplementation 'com.google.firebase:firebase-messaging:' playImplementation 'com.google.firebase:firebase-crashlytics:' - playImplementation 'com.google.firebase:firebase-config' + playImplementation 'com.google.android.play:core:1.10.3' + playImplementation 'com.google.android.play:core-ktx:1.8.1' + playImplementation 'com.google.android.gms:play-services-ads:21.1.0' - playImplementation 'com.google.android.gms:play-services-ads:22.6.0' - playImplementation "com.google.android.play:integrity:1.3.0" - playImplementation 'com.google.android.play:app-update-ktx:2.1.0' - playImplementation 'com.google.android.play:review-ktx:2.0.1' - playImplementation "com.google.android.ump:user-messaging-platform:2.1.0" + hmsImplementation 'com.huawei.hms:hianalytics:6.7.0.300' + hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.1.300' - hmsImplementation 'com.huawei.hms:hianalytics:6.12.0.300' - hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.9.1.303' + releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" - releaseImplementation "com.github.chuckerteam.chucker:library-no-op:$chucker" - - debugImplementation "com.github.chuckerteam.chucker:library:$chucker" + debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker" debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.6' debugImplementation 'com.github.haroldadmin:WhatTheStack:1.0.0-alpha04' @@ -275,17 +263,17 @@ dependencies { testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines" testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - testImplementation 'org.robolectric:robolectric:4.11.1' - testImplementation "androidx.test:runner:1.5.2" - testImplementation "androidx.test.ext:junit:1.1.5" - testImplementation "androidx.test:core:1.5.0" + testImplementation 'org.robolectric:robolectric:4.8.2' + testImplementation "androidx.test:runner:1.4.0" + testImplementation "androidx.test.ext:junit:1.1.3" + testImplementation "androidx.test:core:1.4.0" testImplementation "androidx.room:room-testing:$room" testImplementation "com.google.dagger:hilt-android-testing:$hilt_version" kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version" - androidTestImplementation "androidx.test:core:1.5.0" - androidTestImplementation "androidx.test:runner:1.5.2" - androidTestImplementation "androidx.test.ext:junit:1.1.5" + androidTestImplementation "androidx.test:core:1.4.0" + androidTestImplementation "androidx.test:runner:1.4.0" + androidTestImplementation "androidx.test.ext:junit:1.1.3" androidTestImplementation "io.mockk:mockk-android:$mockk" androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" } diff --git a/app/jacoco.gradle b/app/jacoco.gradle index 67ffdb13b..f253673e6 100644 --- a/app/jacoco.gradle +++ b/app/jacoco.gradle @@ -1,16 +1,16 @@ apply plugin: "jacoco" jacoco { - toolVersion "0.8.11" + toolVersion "0.8.7" reportsDirectory.set(file("$buildDir/reports")) } -tasks.withType(Test).configureEach { +tasks.withType(Test) { jacoco.includeNoLocationClasses = true jacoco.excludes = ['jdk.internal.*'] } -tasks.register('jacocoTestReport', JacocoReport) { +task jacocoTestReport(type: JacocoReport) { group = "Reporting" description = "Generate Jacoco coverage reports" @@ -33,19 +33,19 @@ tasks.register('jacocoTestReport', JacocoReport) { '**/*_Factory.*'] classDirectories.setFrom(fileTree( - dir: "$buildDir/intermediates/classes/debug", - excludes: excludes + dir: "$buildDir/intermediates/classes/debug", + excludes: excludes ) + fileTree( - dir: "$buildDir/tmp/kotlin-classes/fdroidDebug", - excludes: excludes + dir: "$buildDir/tmp/kotlin-classes/fdroidDebug", + excludes: excludes )) sourceDirectories.setFrom(files([ - "src/main/java", - "src/fdroid/java" + "src/main/java", + "src/fdroid/java" ])) executionData.setFrom(fileTree( - dir: project.projectDir, - includes: ["**/*.exec", "**/*.ec"] + dir: project.projectDir, + includes: ["**/*.exec", "**/*.ec"] )) } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 0fd49f6ac..fd9482613 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,6 +1,5 @@ # General -dontobfuscate --ignorewarnings #Config for wulkanowy @@ -25,18 +24,3 @@ #Config for Material Components -keep class com.google.android.material.tabs.** { *; } - - -#Config for HMS SDK --keepattributes *Annotation* --keepattributes Exceptions --keepattributes InnerClasses --keepattributes Signature --keep class com.huawei.agconnect.**{*;} --keep class com.huawei.hianalytics.**{*;} --keep class com.huawei.updatesdk.**{*;} --keep class com.huawei.hms.**{*;} - - -#Config for Wulkanowy SDK --keep,allowobfuscation,allowshrinking class retrofit2.Response diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json deleted file mode 100644 index 129d1917b..000000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json +++ /dev/null @@ -1,2421 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 52, - "identityHash": "8742176f26afcc81279d4a073dca2949", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `userLoginId` INTEGER NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "userLoginId", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "globalKey" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8742176f26afcc81279d4a073dca2949')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json deleted file mode 100644 index 985617872..000000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json +++ /dev/null @@ -1,2439 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 53, - "identityHash": "1dc96a366125ec9f8567da87cdc9c863", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolId", - "columnName": "schoolId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "globalKey" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1dc96a366125ec9f8567da87cdc9c863')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json deleted file mode 100644 index 7b41672b9..000000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json +++ /dev/null @@ -1,2439 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 54, - "identityHash": "1dc96a366125ec9f8567da87cdc9c863", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", - "fields": [ - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "real_id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolId", - "columnName": "schoolId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "globalKey" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1dc96a366125ec9f8567da87cdc9c863')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/55.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/55.json deleted file mode 100644 index 60c2efbe0..000000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/55.json +++ /dev/null @@ -1,2435 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 55, - "identityHash": "cba22eea6d26cf4d6b9a388ba3329a12", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`message_global_key`, `url`, `filename`))", - "fields": [ - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "message_global_key", - "url", - "filename" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolId", - "columnName": "schoolId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "globalKey" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cba22eea6d26cf4d6b9a388ba3329a12')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/56.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/56.json deleted file mode 100644 index 1a26e717b..000000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/56.json +++ /dev/null @@ -1,2442 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 56, - "identityHash": "48f0538bd21601eb5322a7d850e04134", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `scrapper_domain_suffix` TEXT NOT NULL DEFAULT '', `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "scrapperDomainSuffix", - "columnName": "scrapper_domain_suffix", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "''" - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`message_global_key`, `url`, `filename`))", - "fields": [ - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "message_global_key", - "url", - "filename" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolId", - "columnName": "schoolId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "globalKey" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "versionMin", - "columnName": "version_name", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "versionMax", - "columnName": "version_max", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "targetRegisterHost", - "columnName": "target_register_host", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "targetFlavor", - "columnName": "target_flavor", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "destinationUrl", - "columnName": "destination_url", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "priority", - "columnName": "priority", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '48f0538bd21601eb5322a7d850e04134')" - ] - } -} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/57.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/57.json deleted file mode 100644 index 2eff12231..000000000 --- a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/57.json +++ /dev/null @@ -1,2443 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 57, - "identityHash": "d15dbe7d7e4d7df98ec98d9a3a4b5fcd", - "entities": [ - { - "tableName": "Students", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `scrapper_domain_suffix` TEXT NOT NULL DEFAULT '', `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "scrapperBaseUrl", - "columnName": "scrapper_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "scrapperDomainSuffix", - "columnName": "scrapper_domain_suffix", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "''" - }, - { - "fieldPath": "mobileBaseUrl", - "columnName": "mobile_base_url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginType", - "columnName": "login_type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "loginMode", - "columnName": "login_mode", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "certificateKey", - "columnName": "certificate_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "privateKey", - "columnName": "private_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isParent", - "columnName": "is_parent", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "password", - "columnName": "password", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "user_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "student_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolSymbol", - "columnName": "school_id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "school_short", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolName", - "columnName": "school_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "className", - "columnName": "class_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isCurrent", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "registrationDate", - "columnName": "registration_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "nick", - "columnName": "nick", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "avatarColor", - "columnName": "avatar_color", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_Students_email_symbol_student_id_school_id_class_id", - "unique": true, - "columnNames": [ - "email", - "symbol", - "student_id", - "school_id", - "class_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Semesters", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "kindergartenDiaryId", - "columnName": "kindergarten_diary_id", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - }, - { - "fieldPath": "diaryName", - "columnName": "diary_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolYear", - "columnName": "school_year", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterName", - "columnName": "semester_name", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unitId", - "columnName": "unit_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "current", - "columnName": "is_current", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [ - { - "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id", - "unique": true, - "columnNames": [ - "student_id", - "diary_id", - "kindergarten_diary_id", - "semester_id" - ], - "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)" - } - ], - "foreignKeys": [] - }, - { - "tableName": "Exams", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Timetable", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subjectOld", - "columnName": "subjectOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "group", - "columnName": "group", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "room", - "columnName": "room", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "roomOld", - "columnName": "roomOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherOld", - "columnName": "teacherOld", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "info", - "columnName": "info", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "isStudentPlan", - "columnName": "student_plan", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "changes", - "columnName": "changes", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "canceled", - "columnName": "canceled", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Attendance", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "timeId", - "columnName": "time_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excused", - "columnName": "excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deleted", - "columnName": "deleted", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excusable", - "columnName": "excusable", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "excuseStatus", - "columnName": "excuse_status", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AttendanceSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subjectId", - "columnName": "subject_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "month", - "columnName": "month", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "presence", - "columnName": "presence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceExcused", - "columnName": "absence_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "absenceForSchoolReasons", - "columnName": "absence_for_school_reasons", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "lateness", - "columnName": "lateness", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "latenessExcused", - "columnName": "lateness_excused", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "exemption", - "columnName": "exemption", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Grades", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "entry", - "columnName": "entry", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "value", - "columnName": "value", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "modifier", - "columnName": "modifier", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "comment", - "columnName": "comment", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gradeSymbol", - "columnName": "grade_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "description", - "columnName": "description", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weight", - "columnName": "weight", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "weightValue", - "columnName": "weightValue", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesSummary", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "position", - "columnName": "position", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "predictedGrade", - "columnName": "predicted_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalGrade", - "columnName": "final_grade", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "proposedPoints", - "columnName": "proposed_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "finalPoints", - "columnName": "final_points", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pointsSum", - "columnName": "points_sum", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "average", - "columnName": "average", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPredictedGradeNotified", - "columnName": "is_predicted_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isFinalGradeNotified", - "columnName": "is_final_grade_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "predictedGradeLastChange", - "columnName": "predicted_grade_last_change", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "finalGradeLastChange", - "columnName": "final_grade_last_change", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradePartialStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAverage", - "columnName": "class_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAverage", - "columnName": "student_average", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "classAmounts", - "columnName": "class_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentAmounts", - "columnName": "student_amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradesPointsStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "others", - "columnName": "others", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "student", - "columnName": "student", - "affinity": "REAL", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "GradeSemesterStatistics", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "amounts", - "columnName": "amounts", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentGrade", - "columnName": "student_grade", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Messages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)", - "fields": [ - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "mailboxKey", - "columnName": "mailbox_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "messageId", - "columnName": "message_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "correspondents", - "columnName": "correspondents", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "folderId", - "columnName": "folder_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "readBy", - "columnName": "read_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "unreadBy", - "columnName": "unread_by", - "affinity": "INTEGER", - "notNull": false - }, - { - "fieldPath": "hasAttachments", - "columnName": "has_attachments", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sender", - "columnName": "sender", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "recipients", - "columnName": "recipients", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MessageAttachments", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`message_global_key`, `url`, `filename`))", - "fields": [ - { - "fieldPath": "messageGlobalKey", - "columnName": "message_global_key", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "url", - "columnName": "url", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "filename", - "columnName": "filename", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "message_global_key", - "url", - "filename" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "category", - "columnName": "category", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "categoryType", - "columnName": "category_type", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isPointsShow", - "columnName": "is_points_show", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "points", - "columnName": "points", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isRead", - "columnName": "is_read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Homework", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "semesterId", - "columnName": "semester_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "entryDate", - "columnName": "entry_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "attachments", - "columnName": "attachments", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isDone", - "columnName": "is_done", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Subjects", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "realId", - "columnName": "real_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "LuckyNumbers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "luckyNumber", - "columnName": "lucky_number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "CompletedLesson", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "number", - "columnName": "number", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "topic", - "columnName": "topic", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacher", - "columnName": "teacher", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "teacherSymbol", - "columnName": "teacher_symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "substitution", - "columnName": "substitution", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "absence", - "columnName": "absence", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "resources", - "columnName": "resources", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Mailboxes", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))", - "fields": [ - { - "fieldPath": "globalKey", - "columnName": "globalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "symbol", - "columnName": "symbol", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolId", - "columnName": "schoolId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentName", - "columnName": "studentName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolNameShort", - "columnName": "schoolNameShort", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "globalKey" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Recipients", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "mailboxGlobalKey", - "columnName": "mailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "studentMailboxGlobalKey", - "columnName": "studentMailboxGlobalKey", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "fullName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "userName", - "columnName": "userName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "schoolShortName", - "columnName": "schoolShortName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "MobileDevices", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "deviceId", - "columnName": "device_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Teachers", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "shortName", - "columnName": "short_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "School", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "classId", - "columnName": "class_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "name", - "columnName": "name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "contact", - "columnName": "contact", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "headmaster", - "columnName": "headmaster", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "pedagogue", - "columnName": "pedagogue", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Conferences", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "agenda", - "columnName": "agenda", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "presentOnConference", - "columnName": "present_on_conference", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "conferenceId", - "columnName": "conference_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableAdditional", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "start", - "columnName": "start", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "end", - "columnName": "end", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "repeatId", - "columnName": "repeat_id", - "affinity": "BLOB", - "notNull": false, - "defaultValue": "NULL" - }, - { - "fieldPath": "isAddedByUser", - "columnName": "is_added_by_user", - "affinity": "INTEGER", - "notNull": true, - "defaultValue": "0" - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "StudentInfo", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "fullName", - "columnName": "full_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "firstName", - "columnName": "first_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "secondName", - "columnName": "second_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "surname", - "columnName": "surname", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "birthDate", - "columnName": "birth_date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "birthPlace", - "columnName": "birth_place", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "gender", - "columnName": "gender", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "hasPolishCitizenship", - "columnName": "has_polish_citizenship", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "familyName", - "columnName": "family_name", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "parentsNames", - "columnName": "parents_names", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "address", - "columnName": "address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "registeredAddress", - "columnName": "registered_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "correspondenceAddress", - "columnName": "correspondence_address", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "phoneNumber", - "columnName": "phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "cellPhoneNumber", - "columnName": "cell_phone_number", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "email", - "columnName": "email", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "firstGuardian.fullName", - "columnName": "first_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.kinship", - "columnName": "first_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.address", - "columnName": "first_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.phones", - "columnName": "first_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "firstGuardian.email", - "columnName": "first_guardian_email", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.fullName", - "columnName": "second_guardian_full_name", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.kinship", - "columnName": "second_guardian_kinship", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.address", - "columnName": "second_guardian_address", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.phones", - "columnName": "second_guardian_phones", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "secondGuardian.email", - "columnName": "second_guardian_email", - "affinity": "TEXT", - "notNull": false - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "TimetableHeaders", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "diaryId", - "columnName": "diary_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "SchoolAnnouncements", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "userLoginId", - "columnName": "user_login_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "subject", - "columnName": "subject", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "isNotified", - "columnName": "is_notified", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "Notifications", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "fieldPath": "studentId", - "columnName": "student_id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "type", - "columnName": "type", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "destination", - "columnName": "destination", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'" - }, - { - "fieldPath": "date", - "columnName": "date", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "data", - "columnName": "data", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": true, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "AdminMessages", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `types` TEXT NOT NULL DEFAULT '[]', `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": "types", - "columnName": "types", - "affinity": "TEXT", - "notNull": true, - "defaultValue": "'[]'" - }, - { - "fieldPath": "isDismissible", - "columnName": "is_dismissible", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "autoGenerate": false, - "columnNames": [ - "id" - ] - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd15dbe7d7e4d7df98ec98d9a3a4b5fcd')" - ] - } -} \ No newline at end of file diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml index 9c21d49d9..7dbec2cb9 100644 --- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,6 +1,5 @@ - + - - + \ No newline at end of file diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..7dbec2cb9 --- /dev/null +++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..81e723ecc Binary files /dev/null and b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..394b57076 Binary files /dev/null and b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..365b4d663 Binary files /dev/null and b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..463c089b3 Binary files /dev/null and b/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..53d6f5bbd Binary files /dev/null and b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt index 3a3b5948f..461d29951 100644 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt @@ -5,7 +5,6 @@ import android.view.View import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.ui.modules.dashboard.DashboardItem -import kotlinx.coroutines.flow.MutableStateFlow import javax.inject.Inject @Suppress("unused") @@ -14,11 +13,9 @@ class AdsHelper @Inject constructor( private val preferencesRepository: PreferencesRepository ) { - val isMobileAdsSdkInitialized = MutableStateFlow(false) - val canShowAd = false - fun initialize() { preferencesRepository.isAdsEnabled = false + preferencesRepository.isAgreeToProcessData = false preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS } diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt index a3eed484a..3bf7e1693 100644 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -8,7 +8,15 @@ import javax.inject.Singleton @Suppress("UNUSED_PARAMETER") class AnalyticsHelper @Inject constructor() { - fun logEvent(name: String, vararg params: Pair) = Unit - fun setCurrentScreen(activity: Activity, name: String?) = Unit - fun popCurrentScreen(name: String?) = Unit + fun logEvent(name: String, vararg params: Pair) { + // do nothing + } + + fun setCurrentScreen(activity: Activity, name: String?) { + // do nothing + } + + fun popCurrentScreen(name: String?) { + // do nothing + } } diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/InAppUpdateHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/InAppUpdateHelper.kt deleted file mode 100644 index 51b22ec74..000000000 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/InAppUpdateHelper.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.utils - -import android.view.View -import javax.inject.Inject - -class InAppUpdateHelper @Inject constructor() { - - lateinit var messageContainer: View - - fun checkAndInstallUpdates() {} - - fun onResume() {} -} diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/IntegrityHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/IntegrityHelper.kt deleted file mode 100644 index 7af68058c..000000000 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/IntegrityHelper.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.utils - -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class IntegrityHelper @Inject constructor() { - - @Suppress("UNUSED_PARAMETER") - fun getIntegrityToken(requestId: String): String? = null -} diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt deleted file mode 100644 index 88f2598f8..000000000 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.utils - -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper() diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/UpdateHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/UpdateHelper.kt new file mode 100644 index 000000000..3abab9629 --- /dev/null +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/UpdateHelper.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.view.View +import javax.inject.Inject + +@Suppress("UNUSED_PARAMETER") +class UpdateHelper @Inject constructor() { + + lateinit var messageContainer: View + + fun checkAndInstallUpdates(activity: Activity) {} + + fun onActivityResult(requestCode: Int, resultCode: Int) {} + + fun onResume(activity: Activity) {} +} diff --git a/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt index 165a6204f..0e9227022 100644 --- a/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt +++ b/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt @@ -5,7 +5,6 @@ import android.view.View import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.ui.modules.dashboard.DashboardItem -import kotlinx.coroutines.flow.MutableStateFlow import javax.inject.Inject @Suppress("unused") @@ -13,11 +12,10 @@ class AdsHelper @Inject constructor( @ApplicationContext private val context: Context, private val preferencesRepository: PreferencesRepository ) { - val isMobileAdsSdkInitialized = MutableStateFlow(false) - val canShowAd = false fun initialize() { preferencesRepository.isAdsEnabled = false + preferencesRepository.isAgreeToProcessData = false preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS } diff --git a/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt index 1f78931ae..5d33825f1 100644 --- a/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt +++ b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -3,38 +3,26 @@ package io.github.wulkanowy.utils import android.app.Activity import android.content.Context import android.os.Bundle -import com.huawei.agconnect.crash.AGConnectCrash import com.huawei.hms.analytics.HiAnalytics import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.data.repositories.PreferencesRepository import javax.inject.Inject import javax.inject.Singleton @Singleton class AnalyticsHelper @Inject constructor( - @ApplicationContext private val context: Context, - preferencesRepository: PreferencesRepository, - appInfo: AppInfo, + @ApplicationContext private val context: Context ) { private val analytics by lazy { HiAnalytics.getInstance(context) } - private val connectCrash by lazy { AGConnectCrash.getInstance() } - - init { - if (!appInfo.isDebug) { - connectCrash.setUserId(preferencesRepository.installationId) - } - } - fun logEvent(name: String, vararg params: Pair) { Bundle().apply { - params.forEach { (key, value) -> - if (value == null) return@forEach - when (value) { - is String -> putString(key, value) - is Int -> putInt(key, value) - is Boolean -> putBoolean(key, value) + params.forEach { + if (it.second == null) return@forEach + when (it.second) { + is String, is String? -> putString(it.first, it.second as String) + is Int, is Int? -> putInt(it.first, it.second as Int) + is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean) } } analytics.onEvent(name, this) diff --git a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt index 2b1f1d30e..b0c34f413 100644 --- a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt +++ b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt @@ -2,8 +2,7 @@ package io.github.wulkanowy.utils import android.util.Log import com.huawei.agconnect.crash.AGConnectCrash -import fr.bipi.treessence.base.FormatterPriorityTree -import fr.bipi.treessence.common.StackTraceRecorder +import fr.bipi.tressence.base.FormatterPriorityTree class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) { @@ -23,10 +22,16 @@ class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR, ExceptionFilter) override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { if (skipLog(priority, tag, message, t)) return + // Disabled due to a bug in the Huawei library + + /*connectCrash.setCustomKey("priority", priority) + connectCrash.setCustomKey("tag", tag.orEmpty()) + connectCrash.setCustomKey("message", message) + if (t != null) { connectCrash.recordException(t) } else { connectCrash.recordException(StackTraceRecorder(format(priority, tag, message))) - } + }*/ } } diff --git a/app/src/hms/java/io/github/wulkanowy/utils/InAppUpdateHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/InAppUpdateHelper.kt deleted file mode 100644 index 51b22ec74..000000000 --- a/app/src/hms/java/io/github/wulkanowy/utils/InAppUpdateHelper.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.utils - -import android.view.View -import javax.inject.Inject - -class InAppUpdateHelper @Inject constructor() { - - lateinit var messageContainer: View - - fun checkAndInstallUpdates() {} - - fun onResume() {} -} diff --git a/app/src/hms/java/io/github/wulkanowy/utils/IntegrityHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/IntegrityHelper.kt deleted file mode 100644 index 7af68058c..000000000 --- a/app/src/hms/java/io/github/wulkanowy/utils/IntegrityHelper.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.utils - -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class IntegrityHelper @Inject constructor() { - - @Suppress("UNUSED_PARAMETER") - fun getIntegrityToken(requestId: String): String? = null -} diff --git a/app/src/hms/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt deleted file mode 100644 index 88f2598f8..000000000 --- a/app/src/hms/java/io/github/wulkanowy/utils/RemoteConfigHelper.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.wulkanowy.utils - -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper() diff --git a/app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt new file mode 100644 index 000000000..3abab9629 --- /dev/null +++ b/app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.view.View +import javax.inject.Inject + +@Suppress("UNUSED_PARAMETER") +class UpdateHelper @Inject constructor() { + + lateinit var messageContainer: View + + fun checkAndInstallUpdates(activity: Activity) {} + + fun onActivityResult(requestCode: Int, resultCode: Int) {} + + fun onResume(activity: Activity) {} +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 174c9a1fc..7835db902 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,8 +8,7 @@ - - + @@ -37,14 +36,13 @@ + tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"> Resource.mapData(block: (T) -> U) = when (this) { fun Flow>.logResourceStatus(name: String, showData: Boolean = false) = onEach { val description = when (it) { - is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else "" is Resource.Loading -> "started" + is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else "" is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else "" is Resource.Error -> "exception occurred: ${it.error}" } @@ -148,7 +148,7 @@ inline fun networkBoundResource( crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit, crossinline onFetchFailed: (Throwable) -> Unit = { }, crossinline shouldFetch: (ResultType) -> Boolean = { true }, - crossinline mapResult: (ResultType) -> T, + crossinline mapResult: (ResultType) -> T ) = flow { emit(Resource.Loading()) diff --git a/app/src/main/java/io/github/wulkanowy/data/api/SchoolsService.kt b/app/src/main/java/io/github/wulkanowy/data/api/SchoolsService.kt deleted file mode 100644 index a7da9b63c..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/api/SchoolsService.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.wulkanowy.data.api - -import io.github.wulkanowy.data.pojos.IntegrityRequest -import io.github.wulkanowy.data.pojos.LoginEvent -import retrofit2.http.Body -import retrofit2.http.POST -import javax.inject.Singleton - -@Singleton -interface SchoolsService { - - @POST("/log/loginEvent") - suspend fun logLoginEvent(@Body request: IntegrityRequest) -} 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 48a2942c9..15b38805b 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 @@ -47,10 +47,6 @@ import javax.inject.Singleton AutoMigration(from = 44, to = 45), AutoMigration(from = 46, to = 47), AutoMigration(from = 47, to = 48), - AutoMigration(from = 51, to = 52), - AutoMigration(from = 54, to = 55, spec = Migration55::class), - AutoMigration(from = 55, to = 56), - AutoMigration(from = 56, to = 57, spec = Migration57::class), ], version = AppDatabase.VERSION_SCHEMA, exportSchema = true @@ -59,7 +55,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 57 + const val VERSION_SCHEMA = 51 fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf( Migration2(), @@ -109,8 +105,6 @@ abstract class AppDatabase : RoomDatabase() { Migration49(), Migration50(), Migration51(), - Migration53(), - Migration54(), ) fun newInstance( 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 7bc8d12a2..9d3beae1f 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,7 +1,6 @@ package io.github.wulkanowy.data.db import androidx.room.TypeConverter -import io.github.wulkanowy.data.enums.MessageType import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.utils.toTimestamp import kotlinx.serialization.SerializationException @@ -69,9 +68,4 @@ class Converters { @TypeConverter fun stringToDestination(destination: String): Destination = json.decodeFromString(destination) - @TypeConverter - fun messageTypesToString(types: List): String = json.encodeToString(types) - - @TypeConverter - fun stringToMessageTypes(text: String): List = json.decodeFromString(text) } 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 index 2b4cb5975..87f4812da 100644 --- 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 @@ -22,4 +22,4 @@ abstract class AdminMessageDao : BaseDao { deleteAll(oldMessages) insertAll(newMessages) } -} +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt index 084192a07..c44ecd0c2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt @@ -3,16 +3,12 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao import androidx.room.Query import io.github.wulkanowy.data.db.entities.Mailbox -import kotlinx.coroutines.flow.Flow import javax.inject.Singleton @Singleton @Dao interface MailboxDao : BaseDao { - @Query("SELECT * FROM Mailboxes WHERE email = :email") - suspend fun loadAll(email: String): List - - @Query("SELECT * FROM Mailboxes WHERE email = :email AND symbol = :symbol AND schoolId = :schoolId") - fun loadAll(email: String, symbol: String, schoolId: String): Flow> + @Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId ") + suspend fun loadAll(userLoginId: Int): List } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt index 1709f7636..8c730c9bc 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt @@ -16,7 +16,4 @@ interface MessagesDao : BaseDao { @Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC") fun loadAll(mailboxKey: String, folder: Int): Flow> - - @Query("SELECT * FROM Messages WHERE email = :email AND folder_id = :folder ORDER BY date DESC") - fun loadAll(folder: Int, email: String): Flow> } 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 d9326ff6c..87b3e0b32 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt @@ -1,23 +1,17 @@ package io.github.wulkanowy.data.db.dao -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Transaction -import androidx.room.Update -import io.github.wulkanowy.data.db.entities.Semester +import androidx.room.* +import androidx.room.OnConflictStrategy.ABORT import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.StudentName import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import javax.inject.Singleton @Singleton @Dao abstract class StudentDao { - @Insert(onConflict = OnConflictStrategy.ABORT) + @Insert(onConflict = ABORT) abstract suspend fun insertAll(student: List): List @Delete @@ -26,9 +20,6 @@ abstract class StudentDao { @Update(entity = Student::class) abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar) - @Update(entity = Student::class) - abstract suspend fun update(studentName: StudentName) - @Query("SELECT * FROM Students WHERE is_current = 1") abstract suspend fun loadCurrent(): Student? @@ -39,12 +30,12 @@ abstract class StudentDao { abstract suspend fun loadAll(): List @Transaction - @Query("SELECT * FROM Students JOIN Semesters ON Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id") - abstract suspend fun loadStudentsWithSemesters(): Map> + @Query("SELECT * FROM Students") + abstract suspend fun loadStudentsWithSemesters(): List @Transaction - @Query("SELECT * FROM Students JOIN Semesters ON Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id WHERE Students.id = :id") - abstract suspend fun loadStudentWithSemestersById(id: Long): Map> + @Query("SELECT * FROM Students WHERE id = :id") + abstract suspend fun loadStudentWithSemestersById(id: Long): StudentWithSemesters? @Query("UPDATE Students SET is_current = 1 WHERE id = :id") abstract suspend fun updateCurrent(id: Long) @@ -52,9 +43,6 @@ abstract class StudentDao { @Query("UPDATE Students SET is_current = 0") abstract suspend fun resetCurrent() - @Query("DELETE FROM Students WHERE email = :email AND user_name = :userName") - abstract suspend fun deleteByEmailAndUserName(email: String, userName: String) - @Transaction open suspend fun switchCurrent(id: Long) { resetCurrent() diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt index b4b7379f2..5e6eec668 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt @@ -13,7 +13,4 @@ interface TimetableDao : BaseDao { @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow> - - @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/AdminMessage.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/AdminMessage.kt index 875c2a3a5..97fec69b7 100644 --- 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 @@ -3,7 +3,6 @@ package io.github.wulkanowy.data.db.entities import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey -import io.github.wulkanowy.data.enums.MessageType import kotlinx.serialization.Serializable @Serializable @@ -34,8 +33,7 @@ data class AdminMessage( val priority: String, - @ColumnInfo(name = "types", defaultValue = "[]") - val types: List = emptyList(), + val type: String, @ColumnInfo(name = "is_dismissible") val isDismissible: Boolean = false diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt index 2292c3e62..50299e607 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Exam.kt @@ -22,7 +22,6 @@ data class Exam( val subject: String, - @Deprecated("not available anymore") val group: String, val type: String, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt index e65e213dd..7c08e481d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt @@ -1,27 +1,20 @@ package io.github.wulkanowy.data.db.entities -import android.os.Parcelable import androidx.room.Entity import androidx.room.PrimaryKey -import kotlinx.parcelize.Parcelize -@Parcelize @Entity(tableName = "Mailboxes") data class Mailbox( @PrimaryKey val globalKey: String, - - val email: String, - val symbol: String, - val schoolId: String, - val fullName: String, val userName: String, + val userLoginId: Int, val studentName: String, val schoolNameShort: String, val type: MailboxType, -) : java.io.Serializable, Parcelable +) enum class MailboxType { STUDENT, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt index d1356b33d..77874e03d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt @@ -9,9 +9,6 @@ import java.time.Instant @Entity(tableName = "Messages") data class Message( - @ColumnInfo(name = "email") - val email: String, - @ColumnInfo(name = "message_global_key") val messageGlobalKey: String, @@ -32,12 +29,6 @@ data class Message( var unread: Boolean, - @ColumnInfo(name = "read_by") - val readBy: Int?, - - @ColumnInfo(name = "unread_by") - val unreadBy: Int?, - @ColumnInfo(name = "has_attachments") val hasAttachments: Boolean ) : Serializable { diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt index 6f0c84ad7..93f042999 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt @@ -2,14 +2,16 @@ package io.github.wulkanowy.data.db.entities import androidx.room.ColumnInfo import androidx.room.Entity +import androidx.room.PrimaryKey import java.io.Serializable -@Entity( - tableName = "MessageAttachments", - primaryKeys = ["message_global_key", "url", "filename"], -) +@Entity(tableName = "MessageAttachments") data class MessageAttachment( + @PrimaryKey + @ColumnInfo(name = "real_id") + val realId: Int, + @ColumnInfo(name = "message_global_key") val messageGlobalKey: String, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt index e1116733c..76da9643d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt @@ -19,9 +19,6 @@ data class Student( @ColumnInfo(name = "scrapper_base_url") val scrapperBaseUrl: String, - @ColumnInfo(name = "scrapper_domain_suffix", defaultValue = "") - val scrapperDomainSuffix: String, - @ColumnInfo(name = "mobile_base_url") val mobileBaseUrl: String, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentName.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentName.kt deleted file mode 100644 index 46f754b5e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentName.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import java.io.Serializable - -@Entity -data class StudentName( - - @ColumnInfo(name = "student_name") - val studentName: String - -) : Serializable { - - @PrimaryKey - var id: Long = 0 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentWithSemesters.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentWithSemesters.kt index f9869d4e2..9362a954e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentWithSemesters.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentWithSemesters.kt @@ -1,8 +1,13 @@ package io.github.wulkanowy.data.db.entities +import androidx.room.Embedded +import androidx.room.Relation import java.io.Serializable data class StudentWithSemesters( + @Embedded val student: Student, + + @Relation(parentColumn = "student_id", entityColumn = "student_id") val semesters: List ) : Serializable diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt index 0e7e14097..c26a02d1f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration10.kt @@ -5,7 +5,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration10 : Migration(9, 10) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Grades_Summary RENAME TO GradesSummary") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Grades_Summary RENAME TO GradesSummary") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt index 342e2e2e3..6d129bca0 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration11.kt @@ -5,9 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration11 : Migration(10, 11) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS Grades_temp ( id INTEGER PRIMARY KEY NOT NULL, is_read INTEGER NOT NULL, @@ -27,10 +26,9 @@ class Migration11 : Migration(10, 11) { date INTEGER NOT NULL, teacher TEXT NOT NULL ) - """ - ) - db.execSQL("INSERT INTO Grades_temp SELECT * FROM Grades") - db.execSQL("DROP TABLE Grades") - db.execSQL("ALTER TABLE Grades_temp RENAME TO Grades") + """) + database.execSQL("INSERT INTO Grades_temp SELECT * FROM Grades") + database.execSQL("DROP TABLE Grades") + database.execSQL("ALTER TABLE Grades_temp RENAME TO Grades") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt index 6cc726953..c827b82ba 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt @@ -5,17 +5,16 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration12 : Migration(11, 12) { - override fun migrate(db: SupportSQLiteDatabase) { - createTempStudentsTable(db) - replaceStudentTable(db) - updateStudentsWithClassId(db, getStudentsIds(db)) - removeStudentsWithNoClassId(db) - ensureThereIsOnlyOneCurrentStudent(db) + override fun migrate(database: SupportSQLiteDatabase) { + createTempStudentsTable(database) + replaceStudentTable(database) + updateStudentsWithClassId(database, getStudentsIds(database)) + removeStudentsWithNoClassId(database) + ensureThereIsOnlyOneCurrentStudent(database) } - private fun createTempStudentsTable(db: SupportSQLiteDatabase) { - db.execSQL( - """ + private fun createTempStudentsTable(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS Students_tmp ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, endpoint TEXT NOT NULL, @@ -31,16 +30,15 @@ class Migration12 : Migration(11, 12) { registration_date INTEGER NOT NULL, class_id INTEGER NOT NULL ) - """ - ) - db.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students_tmp (email, symbol, student_id, school_id, class_id)") + """) + database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students_tmp (email, symbol, student_id, school_id, class_id)") } - private fun replaceStudentTable(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Students ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") - db.execSQL("INSERT INTO Students_tmp SELECT * FROM Students") - db.execSQL("DROP TABLE Students") - db.execSQL("ALTER TABLE Students_tmp RENAME TO Students") + private fun replaceStudentTable(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") + database.execSQL("INSERT INTO Students_tmp SELECT * FROM Students") + database.execSQL("DROP TABLE Students") + database.execSQL("ALTER TABLE Students_tmp RENAME TO Students") } private fun getStudentsIds(database: SupportSQLiteDatabase): List { @@ -56,18 +54,18 @@ class Migration12 : Migration(11, 12) { return students } - private fun updateStudentsWithClassId(db: SupportSQLiteDatabase, students: List) { + private fun updateStudentsWithClassId(database: SupportSQLiteDatabase, students: List) { students.forEach { - db.execSQL("UPDATE Students SET class_id = IFNULL((SELECT class_id FROM Semesters WHERE student_id = $it), 0) WHERE student_id = $it") + database.execSQL("UPDATE Students SET class_id = IFNULL((SELECT class_id FROM Semesters WHERE student_id = $it), 0) WHERE student_id = $it") } } - private fun removeStudentsWithNoClassId(db: SupportSQLiteDatabase) { - db.execSQL("DELETE FROM Students WHERE class_id = 0") + private fun removeStudentsWithNoClassId(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Students WHERE class_id = 0") } - private fun ensureThereIsOnlyOneCurrentStudent(db: SupportSQLiteDatabase) { - db.execSQL("UPDATE Students SET is_current = 0") - db.execSQL("UPDATE Students SET is_current = 1 WHERE id = (SELECT MAX(id) FROM Students)") + private fun ensureThereIsOnlyOneCurrentStudent(database: SupportSQLiteDatabase) { + database.execSQL("UPDATE Students SET is_current = 0") + database.execSQL("UPDATE Students SET is_current = 1 WHERE id = (SELECT MAX(id) FROM Students)") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt index c5030232b..36de1e837 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt @@ -5,30 +5,27 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration13 : Migration(12, 13) { - override fun migrate(db: SupportSQLiteDatabase) { - addClassNameToStudents(db, getStudentsIds(db)) - updateSemestersTable(db) - markAtLeastAndOnlyOneSemesterAtCurrent(db, getStudentsAndClassIds(db)) - clearMessagesTable(db) + override fun migrate(database: SupportSQLiteDatabase) { + addClassNameToStudents(database, getStudentsIds(database)) + updateSemestersTable(database) + markAtLeastAndOnlyOneSemesterAtCurrent(database, getStudentsAndClassIds(database)) + clearMessagesTable(database) } - private fun addClassNameToStudents( - db: SupportSQLiteDatabase, - students: List> - ) { - db.execSQL("ALTER TABLE Students ADD COLUMN class_name TEXT DEFAULT \"\" NOT NULL") + private fun addClassNameToStudents(database: SupportSQLiteDatabase, students: List>) { + database.execSQL("ALTER TABLE Students ADD COLUMN class_name TEXT DEFAULT \"\" NOT NULL") students.forEach { (id, name) -> val schoolName = name.substringAfter(" - ") val className = name.substringBefore(" - ", "").replace("Klasa ", "") - db.execSQL("UPDATE Students SET class_name = '$className' WHERE id = '$id'") - db.execSQL("UPDATE Students SET school_name = '$schoolName' WHERE id = '$id'") + database.execSQL("UPDATE Students SET class_name = '$className' WHERE id = '$id'") + database.execSQL("UPDATE Students SET school_name = '$schoolName' WHERE id = '$id'") } } - private fun getStudentsIds(db: SupportSQLiteDatabase): MutableList> { + private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList> { val students = mutableListOf>() - db.query("SELECT id, school_name FROM Students").use { + database.query("SELECT id, school_name FROM Students").use { if (it.moveToFirst()) { do { students.add(it.getInt(0) to it.getString(1)) @@ -39,15 +36,15 @@ class Migration13 : Migration(12, 13) { return students } - private fun updateSemestersTable(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Semesters ADD COLUMN school_year INTEGER DEFAULT 1970 NOT NULL") - db.execSQL("ALTER TABLE Semesters ADD COLUMN start INTEGER DEFAULT 0 NOT NULL") - db.execSQL("ALTER TABLE Semesters ADD COLUMN `end` INTEGER DEFAULT 0 NOT NULL") + private fun updateSemestersTable(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Semesters ADD COLUMN school_year INTEGER DEFAULT 1970 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN start INTEGER DEFAULT 0 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN `end` INTEGER DEFAULT 0 NOT NULL") } - private fun getStudentsAndClassIds(db: SupportSQLiteDatabase): List> { + private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List> { val students = mutableListOf>() - db.query("SELECT student_id, class_id FROM Students").use { + database.query("SELECT student_id, class_id FROM Students").use { if (it.moveToFirst()) { do { students.add(it.getInt(0) to it.getInt(1)) @@ -58,17 +55,14 @@ class Migration13 : Migration(12, 13) { return students } - private fun markAtLeastAndOnlyOneSemesterAtCurrent( - db: SupportSQLiteDatabase, - students: List> - ) { + private fun markAtLeastAndOnlyOneSemesterAtCurrent(database: SupportSQLiteDatabase, students: List>) { students.forEach { (studentId, classId) -> - db.execSQL("UPDATE Semesters SET is_current = 0 WHERE student_id = '$studentId' AND class_id = '$classId'") - db.execSQL("UPDATE Semesters SET is_current = 1 WHERE id = (SELECT id FROM Semesters WHERE student_id = '$studentId' AND class_id = '$classId' ORDER BY semester_id DESC)") + database.execSQL("UPDATE Semesters SET is_current = 0 WHERE student_id = '$studentId' AND class_id = '$classId'") + database.execSQL("UPDATE Semesters SET is_current = 1 WHERE id = (SELECT id FROM Semesters WHERE student_id = '$studentId' AND class_id = '$classId' ORDER BY semester_id DESC)") } } - private fun clearMessagesTable(db: SupportSQLiteDatabase) { - db.execSQL("DELETE FROM Messages") + private fun clearMessagesTable(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Messages") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration14.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration14.kt index 793b4a9d2..4dac0d306 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration14.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration14.kt @@ -5,10 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration14 : Migration(13, 14) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS GradesSummary") - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS GradesSummary") + database.execSQL(""" CREATE TABLE IF NOT EXISTS GradesSummary ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, semester_id INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration15.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration15.kt index 5ff44e9ca..5be49a95b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration15.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration15.kt @@ -5,9 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration15 : Migration(14, 15) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS MobileDevices ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, student_id INTEGER NOT NULL, @@ -15,7 +14,6 @@ class Migration15 : Migration(14, 15) { name TEXT NOT NULL, date INTEGER NOT NULL ) - """ - ) + """) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration16.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration16.kt index 8a8f5b8f2..7f40c0f8d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration16.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration16.kt @@ -5,9 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration16 : Migration(15, 16) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS Teachers ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, student_id INTEGER NOT NULL, @@ -16,7 +15,6 @@ class Migration16 : Migration(15, 16) { name TEXT NOT NULL, short_name TEXT NOT NULL ) - """ - ) + """) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration17.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration17.kt index cf3318ad4..e2a2574db 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration17.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration17.kt @@ -5,14 +5,13 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration17 : Migration(16, 17) { - override fun migrate(db: SupportSQLiteDatabase) { - createGradesPointsStatisticsTable(db) - truncateSemestersTable(db) + override fun migrate(database: SupportSQLiteDatabase) { + createGradesPointsStatisticsTable(database) + truncateSemestersTable(database) } - private fun createGradesPointsStatisticsTable(db: SupportSQLiteDatabase) { - db.execSQL( - """ + private fun createGradesPointsStatisticsTable(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS GradesPointsStatistics( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, student_id INTEGER NOT NULL, @@ -21,11 +20,10 @@ class Migration17 : Migration(16, 17) { others REAL NOT NULL, student REAL NOT NULL ) - """ - ) + """) } - private fun truncateSemestersTable(db: SupportSQLiteDatabase) { - db.execSQL("DELETE FROM Semesters") + private fun truncateSemestersTable(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Semesters") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration18.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration18.kt index 713f8e724..6c5e56c6a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration18.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration18.kt @@ -5,9 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration18 : Migration(17, 18) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS School ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, student_id INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration19.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration19.kt index 021cdbb37..d38f1245a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration19.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration19.kt @@ -6,17 +6,16 @@ import io.github.wulkanowy.data.db.SharedPrefProvider class Migration19(private val sharedPrefProvider: SharedPrefProvider) : Migration(18, 19) { - override fun migrate(db: SupportSQLiteDatabase) { - migrateMessages(db) - migrateGrades(db) - migrateStudents(db) + override fun migrate(database: SupportSQLiteDatabase) { + migrateMessages(database) + migrateGrades(database) + migrateStudents(database) migrateSharedPreferences() } - private fun migrateMessages(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE Messages") - db.execSQL( - """ + private fun migrateMessages(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE Messages") + database.execSQL(""" CREATE TABLE IF NOT EXISTS Messages ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, is_notified INTEGER NOT NULL, @@ -35,14 +34,12 @@ class Migration19(private val sharedPrefProvider: SharedPrefProvider) : Migratio read_by INTEGER NOT NULL, removed INTEGER NOT NULL ) - """ - ) + """) } - private fun migrateGrades(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE Grades") - db.execSQL( - """ + private fun migrateGrades(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE Grades") + database.execSQL(""" CREATE TABLE IF NOT EXISTS Grades ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, is_read INTEGER NOT NULL, @@ -62,13 +59,11 @@ class Migration19(private val sharedPrefProvider: SharedPrefProvider) : Migratio date INTEGER NOT NULL, teacher TEXT NOT NULL ) - """ - ) + """) } - private fun migrateStudents(db: SupportSQLiteDatabase) { - db.execSQL( - """ + private fun migrateStudents(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS Students_tmp ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, scrapper_base_url TEXT NOT NULL, @@ -91,29 +86,26 @@ class Migration19(private val sharedPrefProvider: SharedPrefProvider) : Migratio is_current INTEGER NOT NULL, registration_date INTEGER NOT NULL ) - """ - ) + """) - db.execSQL("ALTER TABLE Students ADD COLUMN scrapperBaseUrl TEXT NOT NULL DEFAULT \"\";") - db.execSQL("ALTER TABLE Students ADD COLUMN apiBaseUrl TEXT NOT NULL DEFAULT \"\";") - db.execSQL("ALTER TABLE Students ADD COLUMN is_parent INT NOT NULL DEFAULT 0;") - db.execSQL("ALTER TABLE Students ADD COLUMN loginMode TEXT NOT NULL DEFAULT \"\";") - db.execSQL("ALTER TABLE Students ADD COLUMN certificateKey TEXT NOT NULL DEFAULT \"\";") - db.execSQL("ALTER TABLE Students ADD COLUMN privateKey TEXT NOT NULL DEFAULT \"\";") - db.execSQL("ALTER TABLE Students ADD COLUMN user_login_id INTEGER NOT NULL DEFAULT 0;") + database.execSQL("ALTER TABLE Students ADD COLUMN scrapperBaseUrl TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN apiBaseUrl TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN is_parent INT NOT NULL DEFAULT 0;") + database.execSQL("ALTER TABLE Students ADD COLUMN loginMode TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN certificateKey TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN privateKey TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN user_login_id INTEGER NOT NULL DEFAULT 0;") - db.execSQL( - """ + database.execSQL(""" INSERT INTO Students_tmp( id, scrapper_base_url, mobile_base_url, is_parent, login_type, login_mode, certificate_key, private_key, email, password, symbol, student_id, user_login_id, student_name, school_id, school_name, school_id, school_name, class_name, class_id, is_current, registration_date) SELECT id, endpoint, apiBaseUrl, is_parent, loginType, "SCRAPPER", certificateKey, privateKey, email, password, symbol, student_id, user_login_id, student_name, school_id, school_name, school_id, school_name, class_name, class_id, is_current, registration_date FROM Students - """ - ) - db.execSQL("DROP TABLE Students") - db.execSQL("ALTER TABLE Students_tmp RENAME TO Students") - db.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students (email, symbol, student_id, school_id, class_id)") + """) + database.execSQL("DROP TABLE Students") + database.execSQL("ALTER TABLE Students_tmp RENAME TO Students") + database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students (email, symbol, student_id, school_id, class_id)") } private fun migrateSharedPreferences() { diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt index be8675092..c5a30991a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration2.kt @@ -5,16 +5,14 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration2 : Migration(1, 2) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS LuckyNumbers ( id INTEGER PRIMARY KEY NOT NULL, is_notified INTEGER NOT NULL, student_id INTEGER NOT NULL, date INTEGER NOT NULL, lucky_number INTEGER NOT NULL) - """ - ) + """) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt index 7ad43230b..2fcfc183d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt @@ -5,15 +5,14 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration20 : Migration(19, 20) { - override fun migrate(db: SupportSQLiteDatabase) { - migrateTimetable(db) - truncateSubjects(db) + override fun migrate(database: SupportSQLiteDatabase) { + migrateTimetable(database) + truncateSubjects(database) } - private fun migrateTimetable(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE Timetable") - db.execSQL( - """ + private fun migrateTimetable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE Timetable") + database.execSQL(""" CREATE TABLE IF NOT EXISTS `Timetable` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, @@ -34,11 +33,10 @@ class Migration20 : Migration(19, 20) { `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL ) - """ - ) + """) } - private fun truncateSubjects(db: SupportSQLiteDatabase) { - db.execSQL("DELETE FROM Subjects") + private fun truncateSubjects(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Subjects") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt index 60e044cdc..bc0ff900c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt @@ -5,11 +5,11 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration21 : Migration(20, 21) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Attendance ADD COLUMN excusable INTEGER NOT NULL DEFAULT 0") - db.execSQL("ALTER TABLE Attendance ADD COLUMN time_id INTEGER NOT NULL DEFAULT 0") - db.execSQL("ALTER TABLE Attendance ADD COLUMN excuse_status TEXT DEFAULT NULL") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Attendance ADD COLUMN excusable INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Attendance ADD COLUMN time_id INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Attendance ADD COLUMN excuse_status TEXT DEFAULT NULL") - db.execSQL("DELETE FROM Semesters") + database.execSQL("DELETE FROM Semesters") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration22.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration22.kt index ef525a49d..cf50a6c3e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration22.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration22.kt @@ -5,7 +5,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration22 : Migration(21, 22) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Students ADD COLUMN school_short TEXT NOT NULL DEFAULT ''") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN school_short TEXT NOT NULL DEFAULT ''") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration23.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration23.kt index 3650307a6..22de94c3f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration23.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration23.kt @@ -5,10 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration23 : Migration(22, 23) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Notes ADD COLUMN teacher_symbol TEXT NOT NULL DEFAULT ''") - db.execSQL("ALTER TABLE Notes ADD COLUMN category_type INTEGER NOT NULL DEFAULT 0") - db.execSQL("ALTER TABLE Notes ADD COLUMN is_points_show INTEGER NOT NULL DEFAULT 0") - db.execSQL("ALTER TABLE Notes ADD COLUMN points INTEGER NOT NULL DEFAULT 0") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Notes ADD COLUMN teacher_symbol TEXT NOT NULL DEFAULT ''") + database.execSQL("ALTER TABLE Notes ADD COLUMN category_type INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Notes ADD COLUMN is_points_show INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Notes ADD COLUMN points INTEGER NOT NULL DEFAULT 0") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration24.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration24.kt index a3cd98197..604ed4875 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration24.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration24.kt @@ -5,10 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration24 : Migration(23, 24) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Messages ADD COLUMN has_attachments INTEGER NOT NULL DEFAULT 0") - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Messages ADD COLUMN has_attachments INTEGER NOT NULL DEFAULT 0") + database.execSQL(""" CREATE TABLE IF NOT EXISTS MessageAttachments ( real_id INTEGER NOT NULL, message_id INTEGER NOT NULL, @@ -17,7 +16,6 @@ class Migration24 : Migration(23, 24) { filename TEXT NOT NULL, PRIMARY KEY(real_id) ) - """ - ) + """) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration25.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration25.kt index cb395d7e0..4749bac73 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration25.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration25.kt @@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration25 : Migration(24, 25) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Homework ADD COLUMN is_done INTEGER NOT NULL DEFAULT 0") - db.execSQL("ALTER TABLE Homework ADD COLUMN attachments TEXT NOT NULL DEFAULT \"[]\"") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Homework ADD COLUMN is_done INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Homework ADD COLUMN attachments TEXT NOT NULL DEFAULT \"[]\"") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration26.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration26.kt index 94746b456..7130d86d8 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration26.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration26.kt @@ -5,10 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration26 : Migration(25, 26) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_predicted_grade_notified INTEGER NOT NULL DEFAULT 1") - db.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_final_grade_notified INTEGER NOT NULL DEFAULT 1") - db.execSQL("ALTER TABLE GradesSummary ADD COLUMN predicted_grade_last_change INTEGER NOT NULL DEFAULT 0") - db.execSQL("ALTER TABLE GradesSummary ADD COLUMN final_grade_last_change INTEGER NOT NULL DEFAULT 0") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_predicted_grade_notified INTEGER NOT NULL DEFAULT 1") + database.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_final_grade_notified INTEGER NOT NULL DEFAULT 1") + database.execSQL("ALTER TABLE GradesSummary ADD COLUMN predicted_grade_last_change INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE GradesSummary ADD COLUMN final_grade_last_change INTEGER NOT NULL DEFAULT 0") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt index a7ba763df..5c60beead 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt @@ -5,25 +5,24 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration27 : Migration(26, 27) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Students ADD COLUMN user_name TEXT NOT NULL DEFAULT \"\"") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN user_name TEXT NOT NULL DEFAULT \"\"") - val students = getStudentsIdsAndNames(db) - val units = getReportingUnits(db) + val students = getStudentsIdsAndNames(database) + val units = getReportingUnits(database) students.forEach { (id, userLoginId, studentName) -> - val userNameFromUnits = - units.singleOrNull { (senderId, _) -> senderId == userLoginId }?.second + val userNameFromUnits = units.singleOrNull { (senderId, _) -> senderId == userLoginId }?.second val normalizedStudentName = studentName.split(" ").asReversed().joinToString(" ") val userName = userNameFromUnits ?: normalizedStudentName - db.execSQL("UPDATE Students SET user_name = '$userName' WHERE id = '$id'") + database.execSQL("UPDATE Students SET user_name = '$userName' WHERE id = '$id'") } } - private fun getStudentsIdsAndNames(db: SupportSQLiteDatabase): MutableList> { + private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList> { val students = mutableListOf>() - db.query("SELECT id, user_login_id, student_name FROM Students").use { + database.query("SELECT id, user_login_id, student_name FROM Students").use { if (it.moveToFirst()) { do { students.add(Triple(it.getLong(0), it.getInt(1), it.getString(2))) @@ -34,9 +33,9 @@ class Migration27 : Migration(26, 27) { return students } - private fun getReportingUnits(db: SupportSQLiteDatabase): MutableList> { + private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList> { val units = mutableListOf>() - db.query("SELECT sender_id, sender_name FROM ReportingUnits").use { + database.query("SELECT sender_id, sender_name FROM ReportingUnits").use { if (it.moveToFirst()) { do { units.add(it.getInt(0) to it.getString(1)) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt index e8a5a4a86..51e7628b5 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt @@ -5,9 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration28 : Migration(27, 28) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS Conferences ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, student_id INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt index dac303d27..327552d75 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt @@ -5,10 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration29 : Migration(28, 29) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS GradesStatistics") - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS GradesStatistics") + database.execSQL(""" CREATE TABLE IF NOT EXISTS GradeSemesterStatistics ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, student_id INTEGER NOT NULL, @@ -17,10 +16,8 @@ class Migration29 : Migration(28, 29) { amounts TEXT NOT NULL, student_grade INTEGER NOT NULL ) - """ - ) - db.execSQL( - """ + """) + database.execSQL(""" CREATE TABLE IF NOT EXISTS GradePartialStatistics ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, student_id INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt index 44d421648..d9699c0f4 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration3.kt @@ -5,9 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration3 : Migration(2, 3) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS CompletedLesson ( id INTEGER PRIMARY KEY NOT NULL, student_id INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration30.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration30.kt index 3fea8ec0e..b33914fec 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration30.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration30.kt @@ -5,9 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration30 : Migration(29, 30) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE TimetableAdditional ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, student_id INTEGER NOT NULL, @@ -17,7 +16,6 @@ class Migration30 : Migration(29, 30) { date INTEGER NOT NULL, subject TEXT NOT NULL ) - """ - ) + """) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration31.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration31.kt index 28fb10562..064a3e5bc 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration31.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration31.kt @@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration31 : Migration(30, 31) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( """CREATE TABLE IF NOT EXISTS StudentInfo ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, student_id INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration32.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration32.kt index 347873936..508485e08 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration32.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration32.kt @@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration32 : Migration(31, 32) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Students ADD COLUMN nick TEXT NOT NULL DEFAULT \"\"") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN nick TEXT NOT NULL DEFAULT \"\"") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration33.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration33.kt index 9778d2790..4a57880d4 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration33.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration33.kt @@ -5,10 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration33 : Migration(32, 33) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS StudentInfo") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS StudentInfo") - db.execSQL( + database.execSQL( """CREATE TABLE IF NOT EXISTS StudentInfo ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, student_id INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration34.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration34.kt index e9eec58cd..2c57eb00a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration34.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration34.kt @@ -5,9 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration34 : Migration(33, 34) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("DELETE FROM ReportingUnits") - db.execSQL("DELETE FROM Recipients") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM ReportingUnits") + database.execSQL("DELETE FROM Recipients") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt index b238ce8b4..f63431d00 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt @@ -7,13 +7,13 @@ import io.github.wulkanowy.utils.AppInfo class Migration35(private val appInfo: AppInfo) : Migration(34, 35) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0") - db.query("SELECT * FROM Students").use { + database.query("SELECT * FROM Students").use { while (it.moveToNext()) { val studentId = it.getLongOrNull(0) - db.execSQL( + database.execSQL( """ UPDATE Students SET avatar_color = ${appInfo.defaultColorsForAvatar.random()} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration36.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration36.kt index 62ce346cd..7ea106585 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration36.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration36.kt @@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration36 : Migration(35, 36) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Exams ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") - db.execSQL("ALTER TABLE Homework ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Exams ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") + database.execSQL("ALTER TABLE Homework ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration37.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration37.kt index 9ab35514f..a3fcd51a6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration37.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration37.kt @@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration37 : Migration(36, 37) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( """ CREATE TABLE IF NOT EXISTS TimetableHeaders ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration38.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration38.kt index bb9b32bfa..1f90f5a44 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration38.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration38.kt @@ -5,9 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration38 : Migration(37, 38) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS `SchoolAnnouncements` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, @@ -15,7 +14,6 @@ class Migration38 : Migration(37, 38) { `subject` TEXT NOT NULL, `content` TEXT NOT NULL ) - """ - ) + """) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration39.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration39.kt index 2e5315bf4..6c0d36dd2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration39.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration39.kt @@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration39 : Migration(38, 39) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Conferences ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") - db.execSQL("ALTER TABLE SchoolAnnouncements ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Conferences ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") + database.execSQL("ALTER TABLE SchoolAnnouncements ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") } -} +} \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt index b6089aa62..0ae89bdd6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration4.kt @@ -5,10 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration4 : Migration(3, 4) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS Messages") - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Messages") + database.execSQL(""" CREATE TABLE IF NOT EXISTS Messages ( id INTEGER PRIMARY KEY NOT NULL, is_notified INTEGER NOT NULL, 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 index 8e38b0c84..6d2795c7c 100644 --- 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 @@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration40 : Migration(39, 40) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( """ CREATE TABLE IF NOT EXISTS `Notifications` ( `student_id` INTEGER NOT NULL, @@ -20,4 +20,4 @@ class Migration40 : Migration(39, 40) { """ ) } -} +} \ 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 index bfc28334b..ccaf85755 100644 --- 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 @@ -7,9 +7,9 @@ import io.github.wulkanowy.data.enums.GradeExpandMode class Migration41(private val sharedPrefProvider: SharedPrefProvider) : Migration(40, 41) { - override fun migrate(db: SupportSQLiteDatabase) { + override fun migrate(database: SupportSQLiteDatabase) { migrateSharedPreferences() - db.execSQL("ALTER TABLE Homework ADD COLUMN is_added_by_user INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Homework ADD COLUMN is_added_by_user INTEGER NOT NULL DEFAULT 0") } private fun migrateSharedPreferences() { @@ -18,4 +18,4 @@ class Migration41(private val sharedPrefProvider: SharedPrefProvider) : Migratio } sharedPrefProvider.delete("pref_key_expand_grade") } -} +} \ 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 index 14356e279..3d66f301b 100644 --- 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 @@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration42 : Migration(41, 42) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( """CREATE TABLE IF NOT EXISTS `AdminMessages` ( `id` INTEGER NOT NULL, `title` TEXT NOT NULL, @@ -21,4 +21,4 @@ class Migration42 : Migration(41, 42) { PRIMARY KEY(`id`))""" ) } -} +} \ 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 index ef8108166..68c2834d6 100644 --- 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 @@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration43 : Migration(42, 43) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Timetable ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") - db.execSQL("ALTER TABLE Attendance ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1") + 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 index 0a4e5f962..7bdcab5f4 100644 --- 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 @@ -5,7 +5,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration44 : Migration(43, 44) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE AdminMessages ADD COLUMN is_dismissible INTEGER NOT NULL DEFAULT 0") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE AdminMessages ADD COLUMN is_dismissible INTEGER NOT NULL DEFAULT 0") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt index 0bacbaa0e..d3fa5cf93 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt @@ -8,65 +8,65 @@ import java.time.ZoneOffset class Migration46 : Migration(45, 46) { - override fun migrate(db: SupportSQLiteDatabase) { - migrateConferences(db) - migrateMessages(db) - migrateMobileDevices(db) - migrateNotifications(db) - migrateTimetable(db) - migrateTimetableAdditional(db) + override fun migrate(database: SupportSQLiteDatabase) { + migrateConferences(database) + migrateMessages(database) + migrateMobileDevices(database) + migrateNotifications(database) + migrateTimetable(database) + migrateTimetableAdditional(database) } - private fun migrateConferences(db: SupportSQLiteDatabase) { - db.query("SELECT * FROM Conferences").use { + private fun migrateConferences(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM Conferences").use { while (it.moveToNext()) { val id = it.getLong(it.getColumnIndexOrThrow("id")) val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date")) val timestampUtc = timestampLocal.timestampLocalToUTC() - db.execSQL("UPDATE Conferences SET date = $timestampUtc WHERE id = $id") + database.execSQL("UPDATE Conferences SET date = $timestampUtc WHERE id = $id") } } } - private fun migrateMessages(db: SupportSQLiteDatabase) { - db.query("SELECT * FROM Messages").use { + private fun migrateMessages(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM Messages").use { while (it.moveToNext()) { val id = it.getLong(it.getColumnIndexOrThrow("id")) val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date")) val timestampUtc = timestampLocal.timestampLocalToUTC() - db.execSQL("UPDATE Messages SET date = $timestampUtc WHERE id = $id") + database.execSQL("UPDATE Messages SET date = $timestampUtc WHERE id = $id") } } } - private fun migrateMobileDevices(db: SupportSQLiteDatabase) { - db.query("SELECT * FROM MobileDevices").use { + private fun migrateMobileDevices(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM MobileDevices").use { while (it.moveToNext()) { val id = it.getLong(it.getColumnIndexOrThrow("id")) val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date")) val timestampUtc = timestampLocal.timestampLocalToUTC() - db.execSQL("UPDATE MobileDevices SET date = $timestampUtc WHERE id = $id") + database.execSQL("UPDATE MobileDevices SET date = $timestampUtc WHERE id = $id") } } } - private fun migrateNotifications(db: SupportSQLiteDatabase) { - db.query("SELECT * FROM Notifications").use { + private fun migrateNotifications(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM Notifications").use { while (it.moveToNext()) { val id = it.getLong(it.getColumnIndexOrThrow("id")) val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date")) val timestampUtc = timestampLocal.timestampLocalToUTC() - db.execSQL("UPDATE Notifications SET date = $timestampUtc WHERE id = $id") + database.execSQL("UPDATE Notifications SET date = $timestampUtc WHERE id = $id") } } } - private fun migrateTimetable(db: SupportSQLiteDatabase) { - db.query("SELECT * FROM Timetable").use { + private fun migrateTimetable(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM Timetable").use { while (it.moveToNext()) { val id = it.getLong(it.getColumnIndexOrThrow("id")) val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start")) @@ -74,13 +74,13 @@ class Migration46 : Migration(45, 46) { val timestampUtcStart = timestampLocalStart.timestampLocalToUTC() val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC() - db.execSQL("UPDATE Timetable SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id") + database.execSQL("UPDATE Timetable SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id") } } } - private fun migrateTimetableAdditional(db: SupportSQLiteDatabase) { - db.query("SELECT * FROM TimetableAdditional").use { + private fun migrateTimetableAdditional(database: SupportSQLiteDatabase) { + database.query("SELECT * FROM TimetableAdditional").use { while (it.moveToNext()) { val id = it.getLong(it.getColumnIndexOrThrow("id")) val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start")) @@ -88,7 +88,7 @@ class Migration46 : Migration(45, 46) { val timestampUtcStart = timestampLocalStart.timestampLocalToUTC() val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC() - db.execSQL("UPDATE TimetableAdditional SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id") + database.execSQL("UPDATE TimetableAdditional SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id") } } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt index 97766c01e..6e1de19d4 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt @@ -5,10 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration49 : Migration(48, 49) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS SchoolAnnouncements") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS SchoolAnnouncements") - db.execSQL( + database.execSQL( """ CREATE TABLE IF NOT EXISTS `SchoolAnnouncements` ( `user_login_id` INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt index a5b4e8e1a..dbcd916ba 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration5.kt @@ -7,16 +7,11 @@ import java.time.ZoneOffset class Migration5 : Migration(4, 5) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Students ADD COLUMN registration_date INTEGER DEFAULT 0 NOT NULL") - db.execSQL( - "UPDATE Students SET registration_date = '${ - now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli() - }'" - ) - db.execSQL("DROP TABLE IF EXISTS Notes") - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Students ADD COLUMN registration_date INTEGER DEFAULT 0 NOT NULL") + database.execSQL("UPDATE Students SET registration_date = '${now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli()}'") + database.execSQL("DROP TABLE IF EXISTS Notes") + database.execSQL(""" CREATE TABLE IF NOT EXISTS Notes ( id INTEGER PRIMARY KEY NOT NULL, is_read INTEGER NOT NULL, @@ -26,7 +21,6 @@ class Migration5 : Migration(4, 5) { teacher TEXT NOT NULL, category TEXT NOT NULL, content TEXT NOT NULL) - """ - ) + """) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt index 577998ca0..d45a81570 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt @@ -5,9 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration50 : Migration(49, 50) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS MobileDevices") - db.execSQL( + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS MobileDevices") + database.execSQL( """ CREATE TABLE IF NOT EXISTS `MobileDevices` ( `user_login_id` INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt index 7023049f9..e78e2e3a7 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt @@ -5,17 +5,17 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration51 : Migration(50, 51) { - override fun migrate(db: SupportSQLiteDatabase) { - createMailboxTable(db) - recreateMessagesTable(db) - recreateMessageAttachmentsTable(db) - recreateRecipientsTable(db) - deleteReportingUnitTable(db) + override fun migrate(database: SupportSQLiteDatabase) { + createMailboxTable(database) + recreateMessagesTable(database) + recreateMessageAttachmentsTable(database) + recreateRecipientsTable(database) + deleteReportingUnitTable(database) } - private fun createMailboxTable(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS Mailboxes") - db.execSQL( + private fun createMailboxTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Mailboxes") + database.execSQL( """ CREATE TABLE IF NOT EXISTS `Mailboxes` ( `globalKey` TEXT NOT NULL, @@ -30,9 +30,9 @@ class Migration51 : Migration(50, 51) { ) } - private fun recreateMessagesTable(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS Messages") - db.execSQL( + private fun recreateMessagesTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Messages") + database.execSQL( """ CREATE TABLE IF NOT EXISTS `Messages` ( `message_global_key` TEXT NOT NULL, @@ -52,9 +52,9 @@ class Migration51 : Migration(50, 51) { ) } - private fun recreateMessageAttachmentsTable(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS MessageAttachments") - db.execSQL( + private fun recreateMessageAttachmentsTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS MessageAttachments") + database.execSQL( """ CREATE TABLE IF NOT EXISTS `MessageAttachments` ( `real_id` INTEGER NOT NULL, @@ -66,9 +66,9 @@ class Migration51 : Migration(50, 51) { ) } - private fun recreateRecipientsTable(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS Recipients") - db.execSQL( + private fun recreateRecipientsTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Recipients") + database.execSQL( """ CREATE TABLE IF NOT EXISTS `Recipients` ( `mailboxGlobalKey` TEXT NOT NULL, @@ -82,7 +82,7 @@ class Migration51 : Migration(50, 51) { ) } - private fun deleteReportingUnitTable(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS ReportingUnits") + private fun deleteReportingUnitTable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS ReportingUnits") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt deleted file mode 100644 index dd9e68c97..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration53 : Migration(52, 53) { - - override fun migrate(db: SupportSQLiteDatabase) { - createMailboxTable(db) - recreateMessagesTable(db) - } - - private fun createMailboxTable(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS Mailboxes") - db.execSQL( - """ - CREATE TABLE IF NOT EXISTS `Mailboxes` ( - `globalKey` TEXT NOT NULL, - `email` TEXT NOT NULL, - `symbol` TEXT NOT NULL, - `schoolId` TEXT NOT NULL, - `fullName` TEXT NOT NULL, - `userName` TEXT NOT NULL, - `studentName` TEXT NOT NULL, - `schoolNameShort` TEXT NOT NULL, - `type` TEXT NOT NULL, - PRIMARY KEY(`globalKey`) - )""".trimIndent() - ) - } - - private fun recreateMessagesTable(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS Messages") - db.execSQL( - """ - CREATE TABLE IF NOT EXISTS `Messages` ( - `email` TEXT NOT NULL, - `message_global_key` TEXT NOT NULL, - `mailbox_key` TEXT NOT NULL, - `message_id` INTEGER NOT NULL, - `correspondents` TEXT NOT NULL, - `subject` TEXT NOT NULL, - `date` INTEGER NOT NULL, - `folder_id` INTEGER NOT NULL, - `unread` INTEGER NOT NULL, - `read_by` INTEGER, - `unread_by` INTEGER, - `has_attachments` INTEGER NOT NULL, - `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - `is_notified` INTEGER NOT NULL, - `content` TEXT NOT NULL, - `sender` TEXT, - `recipients` TEXT - )""".trimIndent() - ) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration54.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration54.kt deleted file mode 100644 index 60bd21f0a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration54.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase - -class Migration54 : Migration(53, 54) { - - override fun migrate(db: SupportSQLiteDatabase) { - migrateResman(db) - removeTomaszowMazowieckiStudents(db) - } - - private fun migrateResman(db: SupportSQLiteDatabase) { - db.execSQL( - """ - UPDATE Students SET - scrapper_base_url = 'https://vulcan.net.pl', - login_type = 'ADFSLightScoped', - symbol = 'rzeszowprojekt' - WHERE scrapper_base_url = 'https://resman.pl' - """.trimIndent() - ) - } - - private fun removeTomaszowMazowieckiStudents(db: SupportSQLiteDatabase) { - db.execSQL("DELETE FROM Students WHERE symbol = 'tomaszowmazowiecki'") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration55.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration55.kt deleted file mode 100644 index 424be171b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration55.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.DeleteColumn -import androidx.room.migration.AutoMigrationSpec -import androidx.sqlite.db.SupportSQLiteDatabase - -@DeleteColumn( - tableName = "MessageAttachments", - columnName = "real_id", -) -class Migration55 : AutoMigrationSpec { - - override fun onPostMigrate(db: SupportSQLiteDatabase) { - db.execSQL("DELETE FROM Messages") - db.execSQL("DELETE FROM MessageAttachments") - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration57.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration57.kt deleted file mode 100644 index 2fc8718f9..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration57.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.wulkanowy.data.db.migrations - -import androidx.room.DeleteColumn -import androidx.room.migration.AutoMigrationSpec - -@DeleteColumn( - tableName = "AdminMessages", - columnName = "type", -) -class Migration57 : AutoMigrationSpec diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt index 06cd5f0fd..fa9436187 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration6.kt @@ -5,9 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration6 : Migration(5, 6) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS ReportingUnits ( id INTEGER PRIMARY KEY NOT NULL, student_id INTEGER NOT NULL, @@ -16,11 +15,9 @@ class Migration6 : Migration(5, 6) { sender_id INTEGER NOT NULL, sender_name TEXT NOT NULL, roles TEXT NOT NULL) - """ - ) + """) - db.execSQL( - """ + database.execSQL(""" CREATE TABLE IF NOT EXISTS Recipients ( id INTEGER PRIMARY KEY NOT NULL, student_id INTEGER NOT NULL, @@ -31,11 +28,10 @@ class Migration6 : Migration(5, 6) { unit_id INTEGER NOT NULL, role INTEGER NOT NULL, hash TEXT NOT NULL) - """ - ) + """) - db.execSQL("DELETE FROM Semesters WHERE 1") - db.execSQL("ALTER TABLE Semesters ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") - db.execSQL("ALTER TABLE Semesters ADD COLUMN unit_id INTEGER DEFAULT 0 NOT NULL") + database.execSQL("DELETE FROM Semesters WHERE 1") + database.execSQL("ALTER TABLE Semesters ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL") + database.execSQL("ALTER TABLE Semesters ADD COLUMN unit_id INTEGER DEFAULT 0 NOT NULL") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt index 83a822f22..120716c81 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration7.kt @@ -5,9 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration7 : Migration(6, 7) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" CREATE TABLE IF NOT EXISTS GradesStatistics ( id INTEGER PRIMARY KEY NOT NULL, student_id INTEGER NOT NULL, @@ -16,7 +15,6 @@ class Migration7 : Migration(6, 7) { grade INTEGER NOT NULL, amount INTEGER NOT NULL, is_semester INTEGER NOT NULL) - """ - ) + """) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt index 992e8c68d..7009ee129 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration8.kt @@ -5,9 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration8 : Migration(7, 8) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("ALTER TABLE Timetable ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL") - db.execSQL("ALTER TABLE Timetable ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL") - db.execSQL("ALTER TABLE Timetable ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL") + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Timetable ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL") + database.execSQL("ALTER TABLE Timetable ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL") + database.execSQL("ALTER TABLE Timetable ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL") } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt index b83c34c41..d79a57062 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration9.kt @@ -5,10 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase class Migration9 : Migration(8, 9) { - override fun migrate(db: SupportSQLiteDatabase) { - db.execSQL("DROP TABLE IF EXISTS Messages") - db.execSQL( - """ + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE IF EXISTS Messages") + database.execSQL(""" CREATE TABLE IF NOT EXISTS Messages ( id INTEGER PRIMARY KEY NOT NULL, student_id INTEGER NOT NULL, diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/MessageType.kt b/app/src/main/java/io/github/wulkanowy/data/enums/MessageType.kt deleted file mode 100644 index 531684e4e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/enums/MessageType.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.wulkanowy.data.enums - -enum class MessageType { - GENERAL_MESSAGE, - DASHBOARD_MESSAGE, - LOGIN_MESSAGE, - PASS_RESET_MESSAGE, - ERROR_OVERRIDE, -} diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/TimetableGapsMode.kt b/app/src/main/java/io/github/wulkanowy/data/enums/TimetableGapsMode.kt deleted file mode 100644 index c8310c02d..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/enums/TimetableGapsMode.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.wulkanowy.data.enums - -enum class TimetableGapsMode(val value: String) { - NO_GAPS("no_gaps"), - BETWEEN_LESSONS("between"), - BETWEEN_AND_BEFORE_LESSONS("before_and_between"); - - companion object { - fun getByValue(value: String) = entries.find { it.value == value } ?: BETWEEN_LESSONS - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt index c0ed0c8c2..46e67fdaa 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt @@ -3,22 +3,17 @@ package io.github.wulkanowy.data.mappers import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance import io.github.wulkanowy.sdk.pojo.AttendanceSummary as SdkAttendanceSummary -fun List.mapToEntities(semester: Semester, lessons: List) = map { +fun List.mapToEntities(semester: Semester) = map { Attendance( studentId = semester.studentId, diaryId = semester.diaryId, date = it.date, timeId = it.timeId, number = it.number, - subject = it.subject.ifBlank { - lessons.find { lesson -> - lesson.date == it.date && lesson.number == it.number - }?.subject.orEmpty() - }, + subject = it.subject, name = it.name, presence = it.presence, absence = it.absence, diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt index add6439d4..17a9e5cdb 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt @@ -10,9 +10,9 @@ fun List.mapToEntities(semester: Semester) = map { diaryId = semester.diaryId, agenda = it.agenda, conferenceId = it.id, - date = it.date.toInstant(), + date = it.dateZoned.toInstant(), presentOnConference = it.presentOnConference, - subject = it.topic, - title = it.place, + subject = it.subject, + title = it.title ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt index 173dfebf9..bdb5efbba 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt @@ -11,7 +11,7 @@ fun List.mapToEntities(semester: Semester) = map { date = it.date, entryDate = it.entryDate, subject = it.subject, - group = "", + group = it.group, type = it.type, description = it.description, teacher = it.teacher, diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt index 0cf547770..2ccca1b90 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt @@ -10,11 +10,9 @@ fun List.mapToEntities(student: Student) = map { globalKey = it.globalKey, fullName = it.fullName, userName = it.userName, + userLoginId = student.userLoginId, studentName = it.studentName, schoolNameShort = it.schoolNameShort, type = MailboxType.valueOf(it.type.name), - email = student.email, - symbol = student.symbol, - schoolId = student.schoolSymbol, ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt index a26d76651..2e7967f0e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt @@ -2,36 +2,21 @@ package io.github.wulkanowy.data.mappers import io.github.wulkanowy.data.db.entities.* import io.github.wulkanowy.sdk.pojo.MailboxType -import timber.log.Timber import io.github.wulkanowy.sdk.pojo.Message as SdkMessage import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient -fun List.mapToEntities( - student: Student, - mailbox: Mailbox?, - allMailboxes: List -): List = map { +fun List.mapToEntities(mailbox: Mailbox) = map { Message( messageGlobalKey = it.globalKey, - mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box -> - box.fullName == it.mailbox - }?.globalKey.let { mailboxKey -> - if (mailboxKey == null) { - Timber.e("Can't find ${it.mailbox} in $allMailboxes") - "unknown" - } else mailboxKey - }, - email = student.email, + mailboxKey = mailbox.globalKey, messageId = it.id, correspondents = it.correspondents, subject = it.subject.trim(), - date = it.date.toInstant(), + date = it.dateZoned.toInstant(), folderId = it.folderId, unread = it.unread, - unreadBy = it.unreadBy, - readBy = it.readBy, - hasAttachments = it.hasAttachments, + hasAttachments = it.hasAttachments ).apply { content = it.content.orEmpty() } @@ -40,6 +25,7 @@ fun List.mapToEntities( fun List.mapToEntities(messageGlobalKey: String) = map { MessageAttachment( messageGlobalKey = messageGlobalKey, + realId = it.url.hashCode(), url = it.url, filename = it.filename ) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt index 1f4178fae..1a1c501f6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt @@ -9,7 +9,7 @@ import io.github.wulkanowy.sdk.pojo.Token as SdkToken fun List.mapToEntities(student: Student) = map { MobileDevice( userLoginId = student.userLoginId, - date = it.createDate.toInstant(), + date = it.createDateZoned.toInstant(), deviceId = it.id, name = it.name ) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/RegisterUserMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/RegisterUserMapper.kt deleted file mode 100644 index 72c4861c9..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/RegisterUserMapper.kt +++ /dev/null @@ -1,90 +0,0 @@ -package io.github.wulkanowy.data.mappers - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.data.pojos.* -import java.time.Instant -import io.github.wulkanowy.sdk.pojo.RegisterStudent as SdkRegisterStudent -import io.github.wulkanowy.sdk.pojo.RegisterUser as SdkRegisterUser - -fun SdkRegisterUser.mapToPojo(password: String?) = RegisterUser( - email = email, - login = login, - password = password, - scrapperBaseUrl = scrapperBaseUrl, - loginMode = loginMode, - loginType = loginType, - symbols = symbols.map { registerSymbol -> - RegisterSymbol( - symbol = registerSymbol.symbol, - error = registerSymbol.error, - hebeBaseUrl = registerSymbol.hebeBaseUrl, - keyId = registerSymbol.keyId, - privatePem = registerSymbol.privatePem, - userName = registerSymbol.userName, - schools = registerSymbol.schools.map { - RegisterUnit( - userLoginId = it.userLoginId, - schoolId = it.schoolId, - schoolName = it.schoolName, - schoolShortName = it.schoolShortName, - parentIds = it.parentIds, - studentIds = it.studentIds, - employeeIds = it.employeeIds, - error = it.error, - students = it.subjects - .filterIsInstance() - .map { registerSubject -> - RegisterStudent( - studentId = registerSubject.studentId, - studentName = registerSubject.studentName, - studentSecondName = registerSubject.studentSecondName, - studentSurname = registerSubject.studentSurname, - className = registerSubject.className, - classId = registerSubject.classId, - isParent = registerSubject.isParent, - semesters = registerSubject.semesters - .mapToEntities(registerSubject.studentId), - ) - }, - ) - } - ) - }, -) - -fun RegisterStudent.mapToStudentWithSemesters( - user: RegisterUser, - scrapperDomainSuffix: String, - symbol: RegisterSymbol, - unit: RegisterUnit, - colors: List, -): StudentWithSemesters = StudentWithSemesters( - semesters = semesters, - student = Student( - email = user.login, // for compatibility - userName = symbol.userName, - userLoginId = unit.userLoginId, - isParent = isParent, - className = className, - classId = classId, - studentId = studentId, - symbol = symbol.symbol, - loginType = user.loginType?.name.orEmpty(), - schoolName = unit.schoolName, - schoolShortName = unit.schoolShortName, - schoolSymbol = unit.schoolId, - studentName = "$studentName $studentSurname", - loginMode = user.loginMode.name, - scrapperBaseUrl = user.scrapperBaseUrl.orEmpty(), - scrapperDomainSuffix = scrapperDomainSuffix, - mobileBaseUrl = symbol.hebeBaseUrl.orEmpty(), - certificateKey = symbol.keyId.orEmpty(), - privateKey = symbol.privatePem.orEmpty(), - password = user.password.orEmpty(), - isCurrent = false, - registrationDate = Instant.now(), - ).apply { - avatarColor = colors.random() - }, -) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt new file mode 100644 index 000000000..a2110d7f5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt @@ -0,0 +1,37 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import java.time.Instant +import io.github.wulkanowy.sdk.pojo.Student as SdkStudent + +fun List.mapToEntities(password: String = "", colors: List) = map { + StudentWithSemesters( + student = Student( + email = it.email, + password = password, + isParent = it.isParent, + symbol = it.symbol, + studentId = it.studentId, + userLoginId = it.userLoginId, + userName = it.userName, + studentName = it.studentName + " " + it.studentSurname, + schoolSymbol = it.schoolSymbol, + schoolShortName = it.schoolShortName, + schoolName = it.schoolName, + className = it.className, + classId = it.classId, + scrapperBaseUrl = it.scrapperBaseUrl, + loginType = it.loginType.name, + isCurrent = false, + registrationDate = Instant.now(), + mobileBaseUrl = it.mobileBaseUrl, + privateKey = it.privateKey, + certificateKey = it.certificateKey, + loginMode = it.loginMode.name, + ).apply { + avatarColor = colors.random() + }, + semesters = it.semesters.mapToEntities(it.studentId) + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt index ee525e108..e55aa3cf7 100644 --- a/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt @@ -5,10 +5,10 @@ import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.TimetableAdditional import io.github.wulkanowy.data.db.entities.TimetableHeader import io.github.wulkanowy.data.pojos.TimetableFull -import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetableFull +import io.github.wulkanowy.sdk.pojo.TimetableFull as SdkTimetableFull import io.github.wulkanowy.sdk.pojo.TimetableDayHeader as SdkTimetableHeader -import io.github.wulkanowy.sdk.pojo.Lesson as SdkLesson -import io.github.wulkanowy.sdk.pojo.LessonAdditional as SdkTimetableAdditional +import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable +import io.github.wulkanowy.sdk.pojo.TimetableAdditional as SdkTimetableAdditional fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull( lessons = lessons.mapToEntities(semester), @@ -16,13 +16,13 @@ fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull( headers = headers.mapToEntities(semester) ) -fun List.mapToEntities(semester: Semester) = map { +fun List.mapToEntities(semester: Semester) = map { Timetable( studentId = semester.studentId, diaryId = semester.diaryId, number = it.number, - start = it.start.toInstant(), - end = it.end.toInstant(), + start = it.startZoned.toInstant(), + end = it.endZoned.toInstant(), date = it.date, subject = it.subject, subjectOld = it.subjectOld, @@ -45,8 +45,8 @@ fun List.mapToEntities(semester: Semester) = map { diaryId = semester.diaryId, subject = it.subject, date = it.date, - start = it.start.toInstant(), - end = it.end.toInstant(), + start = it.startZoned.toInstant(), + end = it.endZoned.toInstant(), ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/LoginEvent.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/LoginEvent.kt deleted file mode 100644 index c2b4d2ded..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/pojos/LoginEvent.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.wulkanowy.data.pojos - -import kotlinx.serialization.Serializable - -@Serializable -data class LoginEvent( - val uuid: String, - val schoolName: String, - val schoolShort: String, - val schoolAddress: String, - val scraperBaseUrl: String, - val symbol: String, - val schoolId: String, - val loginType: String, -) - -@Serializable -data class IntegrityRequest( - val tokenString: String, - val data: T, -) diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/RegisterUser.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/RegisterUser.kt deleted file mode 100644 index 98bf1402b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/pojos/RegisterUser.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.data.pojos - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.sdk.scrapper.Scrapper - -data class RegisterUser( - val email: String, - val password: String?, - val login: String, // may be the same as email - val scrapperBaseUrl: String?, - val loginType: Scrapper.LoginType?, - val loginMode: Sdk.Mode, - val symbols: List, -) : java.io.Serializable - -data class RegisterSymbol( - val symbol: String, - val error: Throwable?, - val hebeBaseUrl: String?, - val keyId: String?, - val privatePem: String?, - val userName: String, - val schools: List, -) : java.io.Serializable - -data class RegisterUnit( - val userLoginId: Int, - val schoolId: String, - val schoolName: String, - val schoolShortName: String, - val parentIds: List, - val studentIds: List, - val employeeIds: List, - val error: Throwable?, - val students: List, -) : java.io.Serializable - -data class RegisterStudent( - val studentId: Int, - val studentName: String, - val studentSecondName: String, - val studentSurname: String, - val className: String, - val classId: Int, - val isParent: Boolean, - val semesters: List, -) : java.io.Serializable diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt index b831ee755..c9655b722 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt @@ -1,11 +1,10 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.Resource import io.github.wulkanowy.data.api.AdminMessageService import io.github.wulkanowy.data.db.dao.AdminMessageDao -import io.github.wulkanowy.data.db.entities.AdminMessage +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.networkBoundResource -import kotlinx.coroutines.flow.Flow +import io.github.wulkanowy.utils.AppInfo import kotlinx.coroutines.sync.Mutex import javax.inject.Inject import javax.inject.Singleton @@ -14,20 +13,34 @@ import javax.inject.Singleton class AdminMessageRepository @Inject constructor( private val adminMessageService: AdminMessageService, private val adminMessageDao: AdminMessageDao, + private val appInfo: AppInfo ) { - private val saveFetchResultMutex = Mutex() - fun getAdminMessages(): Flow>> = - networkBoundResource( - mutex = saveFetchResultMutex, - isResultEmpty = { false }, - query = { adminMessageDao.loadAll() }, - fetch = { adminMessageService.getAdminMessages() }, - shouldFetch = { true }, - saveFetchResult = { oldItems, newItems -> - adminMessageDao.removeOldAndSaveNew(oldItems, newItems) - }, - showSavedOnLoading = false, - ) + suspend fun getAdminMessages(student: Student) = networkBoundResource( + mutex = saveFetchResultMutex, + isResultEmpty = { it == null }, + query = { adminMessageDao.loadAll() }, + fetch = { adminMessageService.getAdminMessages() }, + shouldFetch = { true }, + saveFetchResult = { oldItems, newItems -> + adminMessageDao.removeOldAndSaveNew(oldItems, newItems) + }, + showSavedOnLoading = false, + mapResult = { adminMessages -> + adminMessages.filter { adminMessage -> + val isCorrectRegister = adminMessage.targetRegisterHost?.let { + student.scrapperBaseUrl.contains(it, true) + } ?: true + val isCorrectFlavor = + adminMessage.targetFlavor?.equals(appInfo.buildFlavor, true) ?: true + val isCorrectMaxVersion = + adminMessage.versionMax?.let { it >= appInfo.versionCode } ?: true + val isCorrectMinVersion = + adminMessage.versionMin?.let { it <= appInfo.versionCode } ?: true + + isCorrectRegister && isCorrectFlavor && isCorrectMaxVersion && isCorrectMinVersion + }.maxByOrNull { it.id } + } + ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt index bec2797db..cbaa12bd3 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 @@ -19,6 +19,7 @@ class AppCreatorRepository @Inject constructor( ) { @OptIn(ExperimentalSerializationApi::class) + @Suppress("BlockingMethodInNonBlockingContext") suspend fun getAppCreators() = withContext(dispatchers.io) { val inputStream = context.assets.open("contributors.json").buffered() json.decodeFromStream>(inputStream) 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 3afb99077..9aa6562a6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt @@ -1,7 +1,6 @@ package io.github.wulkanowy.data.repositories import io.github.wulkanowy.data.db.dao.AttendanceDao -import io.github.wulkanowy.data.db.dao.TimetableDao import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student @@ -10,10 +9,8 @@ import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Absent import io.github.wulkanowy.utils.* -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.withContext import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime @@ -23,7 +20,6 @@ import javax.inject.Singleton @Singleton class AttendanceRepository @Inject constructor( private val attendanceDb: AttendanceDao, - private val timetableDb: TimetableDao, private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -52,15 +48,10 @@ class AttendanceRepository @Inject constructor( attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) }, fetch = { - val lessons = withContext(Dispatchers.IO) { - timetableDb.load( - semester.diaryId, semester.studentId, start.monday, end.sunday - ) - } sdk.init(student) .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getAttendance(start.monday, end.sunday) - .mapToEntities(semester, lessons) + .getAttendance(start.monday, end.sunday, semester.semesterId) + .mapToEntities(semester) }, saveFetchResult = { old, new -> attendanceDb.deleteAll(old uniqueSubtract new) 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 013c0951d..faa80b93e 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 @@ -52,7 +52,7 @@ class ExamRepository @Inject constructor( fetch = { sdk.init(student) .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getExams(start.startExamsDay, start.endExamsDay) + .getExams(start.startExamsDay, start.endExamsDay, semester.semesterId) .mapToEntities(semester) }, saveFetchResult = { old, new -> 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 4ff4517d0..87e8410f1 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 @@ -35,15 +35,12 @@ class LuckyNumberRepository @Inject constructor( fetch = { sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student) }, - saveFetchResult = { oldLuckyNumber, newLuckyNumber -> - newLuckyNumber ?: return@networkBoundResource - - if (newLuckyNumber != oldLuckyNumber) { - val updatedLuckNumberList = - listOf(newLuckyNumber.apply { if (notify) isNotified = false }) - - oldLuckyNumber?.let { luckyNumberDb.deleteAll(listOfNotNull(it)) } - luckyNumberDb.insertAll(updatedLuckNumberList) + saveFetchResult = { old, new -> + if (new != old) { + old?.let { luckyNumberDb.deleteAll(listOfNotNull(it)) } + luckyNumberDb.insertAll(listOfNotNull((new?.apply { + if (notify) isNotified = false + }))) } } ) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt new file mode 100644 index 000000000..ad4f669e2 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt @@ -0,0 +1,83 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.MailboxDao +import io.github.wulkanowy.data.db.entities.Mailbox +import io.github.wulkanowy.data.db.entities.Student +import 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 +import javax.inject.Singleton + +@Singleton +class MailboxRepository @Inject constructor( + private val mailboxDao: MailboxDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + private val cacheKey = "mailboxes" + + suspend fun refreshMailboxes(student: Student) { + val new = sdk.init(student).getMailboxes().mapToEntities(student) + val old = mailboxDao.loadAll(student.userLoginId) + + mailboxDao.deleteAll(old uniqueSubtract new) + mailboxDao.insertAll(new uniqueSubtract old) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) + } + + suspend fun getMailbox(student: Student): Mailbox { + val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) + val mailboxes = mailboxDao.loadAll(student.userLoginId) + val mailbox = mailboxes.filterByStudent(student) + + return if (isExpired || mailbox == null) { + refreshMailboxes(student) + val newMailbox = mailboxDao.loadAll(student.userLoginId).filterByStudent(student) + + requireNotNull(newMailbox) { + "Mailbox for ${student.userName} - ${student.studentName} not found! Saved mailboxes: $mailboxes" + } + + newMailbox + } else mailbox + } + + private fun List.filterByStudent(student: Student): Mailbox? { + val normalizedStudentName = student.studentName.normalizeStudentName() + + return find { + it.studentName.normalizeStudentName() == normalizedStudentName + } ?: singleOrNull { + it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart() + } ?: singleOrNull { + it.studentName.getUnauthorizedVersion() == normalizedStudentName + } + } + + private fun String.normalizeStudentName(): String { + return trim().split(" ").joinToString(" ") { part -> + part.lowercase().replaceFirstChar { it.uppercase() } + } + } + + private fun String.getFirstAndLastPart(): String { + val parts = normalizeStudentName().split(" ") + + val endParts = parts.filterIndexed { i, _ -> + i == 0 || parts.size == i - 1 + } + return endParts.joinToString(" ") + } + + private fun String.getUnauthorizedVersion(): String { + return normalizeStudentName().split(" ") + .joinToString(" ") { + it.first() + "*".repeat(it.length - 1) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt index c8fccb23d..e7428762e 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 @@ -5,25 +5,16 @@ import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.Resource import io.github.wulkanowy.data.db.SharedPrefProvider -import io.github.wulkanowy.data.db.dao.MailboxDao import io.github.wulkanowy.data.db.dao.MessageAttachmentDao import io.github.wulkanowy.data.db.dao.MessagesDao -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.MessageWithAttachment -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.* import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED import io.github.wulkanowy.data.enums.MessageFolder.TRASHED import io.github.wulkanowy.data.mappers.mapFromEntities import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource -import io.github.wulkanowy.data.onResourceError -import io.github.wulkanowy.data.onResourceSuccess import io.github.wulkanowy.data.pojos.MessageDraft -import io.github.wulkanowy.data.waitForResult -import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Folder import io.github.wulkanowy.utils.AutoRefreshHelper @@ -33,6 +24,7 @@ import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.sync.Mutex +import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import timber.log.Timber @@ -48,18 +40,16 @@ class MessageRepository @Inject constructor( private val refreshHelper: AutoRefreshHelper, private val sharedPrefProvider: SharedPrefProvider, private val json: Json, - private val mailboxDao: MailboxDao, - private val getMailboxByStudentUseCase: GetMailboxByStudentUseCase, ) { private val saveFetchResultMutex = Mutex() - private val messagesCacheKey = "message" - private val mailboxCacheKey = "mailboxes" + private val cacheKey = "message" + @Suppress("UNUSED_PARAMETER") fun getMessages( student: Student, - mailbox: Mailbox?, + mailbox: Mailbox, folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false, @@ -68,20 +58,16 @@ class MessageRepository @Inject constructor( isResultEmpty = { it.isEmpty() }, shouldFetch = { val isExpired = refreshHelper.shouldBeRefreshed( - key = getRefreshKey(messagesCacheKey, mailbox, folder) + key = getRefreshKey(cacheKey, student, folder) ) it.isEmpty() || forceRefresh || isExpired }, - query = { - if (mailbox == null) { - messagesDb.loadAll(folder.id, student.email) - } else messagesDb.loadAll(mailbox.globalKey, folder.id) - }, + query = { messagesDb.loadAll(mailbox.globalKey, folder.id) }, fetch = { sdk.init(student).getMessages( folder = Folder.valueOf(folder.name), - mailboxKey = mailbox?.globalKey, - ).mapToEntities(student, mailbox, mailboxDao.loadAll(student.email)) + mailboxKey = mailbox.globalKey, + ).mapToEntities(mailbox) }, saveFetchResult = { old, new -> messagesDb.deleteAll(old uniqueSubtract new) @@ -89,9 +75,7 @@ class MessageRepository @Inject constructor( it.isNotified = !notify }) - refreshHelper.updateLastRefreshTimestamp( - getRefreshKey(messagesCacheKey, mailbox, folder) - ) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder)) } ) @@ -104,26 +88,18 @@ class MessageRepository @Inject constructor( shouldFetch = { checkNotNull(it) { "This message no longer exist!" } Timber.d("Message content in db empty: ${it.message.content.isBlank()}") - (it.message.unread && markAsRead) || it.message.content.isBlank() - }, - query = { - messagesDb.loadMessageWithAttachment(message.messageGlobalKey) + it.message.unread || it.message.content.isBlank() }, + query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) }, fetch = { - sdk.init(student).getMessageDetails( - messageKey = it!!.message.messageGlobalKey, - markAsRead = message.unread && markAsRead, - ) + sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey, markAsRead) }, saveFetchResult = { old, new -> checkNotNull(old) { "Fetched message no longer exist!" } messagesDb.updateAll( listOf(old.message.apply { id = message.id - unread = when { - markAsRead -> false - else -> unread - } + unread = !markAsRead sender = new.sender recipients = new.recipients.singleOrNull() ?: "Wielu adresatów" content = content.ifBlank { new.content } @@ -133,14 +109,12 @@ class MessageRepository @Inject constructor( items = new.attachments.mapToEntities(message.messageGlobalKey), ) - Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read: $markAsRead") + Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read") } ) - fun getMessagesFromDatabase(student: Student, mailbox: Mailbox?): Flow> { - return if (mailbox == null) { - messagesDb.loadAll(RECEIVED.id, student.email) - } else messagesDb.loadAll(mailbox.globalKey, RECEIVED.id) + fun getMessagesFromDatabase(mailbox: Mailbox): Flow> { + return messagesDb.loadAll(mailbox.globalKey, RECEIVED.id) } suspend fun updateMessages(messages: List) { @@ -162,7 +136,7 @@ class MessageRepository @Inject constructor( ) } - suspend fun deleteMessages(student: Student, mailbox: Mailbox?, messages: List) { + suspend fun deleteMessages(student: Student, mailbox: Mailbox, messages: List) { val firstMessage = messages.first() sdk.init(student).deleteMessages( messages = messages.map { it.messageGlobalKey }, @@ -191,44 +165,10 @@ class MessageRepository @Inject constructor( ).first() } - suspend fun deleteMessage(student: Student, mailbox: Mailbox?, message: Message) { + suspend fun deleteMessage(student: Student, mailbox: Mailbox, message: Message) { deleteMessages(student, mailbox, listOf(message)) } - suspend fun getMailboxes(student: Student, forceRefresh: Boolean) = networkBoundResource( - mutex = saveFetchResultMutex, - isResultEmpty = { it.isEmpty() }, - shouldFetch = { - val isExpired = refreshHelper.shouldBeRefreshed( - key = getRefreshKey(mailboxCacheKey, student), - ) - it.isEmpty() || isExpired || forceRefresh - }, - query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) }, - fetch = { - sdk.init(student).getMailboxes().mapToEntities(student) - }, - saveFetchResult = { old, new -> - mailboxDao.deleteAll(old uniqueSubtract new) - mailboxDao.insertAll(new uniqueSubtract old) - - refreshHelper.updateLastRefreshTimestamp(getRefreshKey(mailboxCacheKey, student)) - } - ) - - suspend fun getMailboxByStudent(student: Student): Mailbox? { - val mailbox = getMailboxByStudentUseCase(student) - - return if (mailbox == null) { - getMailboxes(student, forceRefresh = true) - .onResourceError { throw it } - .onResourceSuccess { Timber.i("Found ${it.size} new mailboxes") } - .waitForResult() - - getMailboxByStudentUseCase(student) - } else mailbox - } - var draftMessage: MessageDraft? get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_draft)) ?.let { json.decodeFromString(it) } 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 4101803f3..e5d7bc5cb 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 @@ -42,7 +42,7 @@ class NoteRepository @Inject constructor( fetch = { sdk.init(student) .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getNotes() + .getNotes(semester.semesterId) .mapToEntities(semester) }, saveFetchResult = { old, new -> 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 64e60a60b..486538e0c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt @@ -2,31 +2,25 @@ package io.github.wulkanowy.data.repositories import android.content.Context import android.content.SharedPreferences -import androidx.annotation.StringRes import androidx.core.content.edit import com.fredporciuncula.flow.preferences.FlowSharedPreferences import com.fredporciuncula.flow.preferences.Preference -import com.fredporciuncula.flow.preferences.Serializer import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R -import io.github.wulkanowy.data.enums.AppTheme -import io.github.wulkanowy.data.enums.GradeColorTheme -import io.github.wulkanowy.data.enums.GradeExpandMode -import io.github.wulkanowy.data.enums.GradeSortingMode -import io.github.wulkanowy.data.enums.TimetableGapsMode -import io.github.wulkanowy.data.enums.TimetableMode +import io.github.wulkanowy.data.enums.* import io.github.wulkanowy.ui.modules.dashboard.DashboardItem import io.github.wulkanowy.ui.modules.grade.GradeAverageMode -import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import java.time.Instant -import java.util.UUID import javax.inject.Inject import javax.inject.Singleton +@OptIn(ExperimentalCoroutinesApi::class) @Singleton class PreferencesRepository @Inject constructor( @ApplicationContext val context: Context, @@ -35,35 +29,29 @@ class PreferencesRepository @Inject constructor( private val json: Json, ) { + val startMenuIndex: Int + get() = getString(R.string.pref_key_start_menu, R.string.pref_default_startup).toInt() + val isShowPresent: Boolean get() = getBoolean( R.string.pref_key_attendance_present, R.bool.pref_default_attendance_present ) - private val gradeAverageModePref: Preference - get() = getObjectFlow( - R.string.pref_key_grade_average_mode, - R.string.pref_default_grade_average_mode, - object : Serializer { - override fun serialize(value: GradeAverageMode) = value.value - override fun deserialize(serialized: String) = - GradeAverageMode.getByValue(serialized) - }, + val gradeAverageMode: GradeAverageMode + get() = GradeAverageMode.getByValue( + getString( + R.string.pref_key_grade_average_mode, + R.string.pref_default_grade_average_mode + ) ) - val gradeAverageModeFlow: Flow - get() = gradeAverageModePref.asFlow() - - private val gradeAverageForceCalcPref: Preference - get() = flowSharedPref.getBoolean( - context.getString(R.string.pref_key_grade_average_force_calc), - context.resources.getBoolean(R.bool.pref_default_grade_average_force_calc) + val gradeAverageForceCalc: Boolean + get() = getBoolean( + R.string.pref_key_grade_average_force_calc, + R.bool.pref_default_grade_average_force_calc ) - val gradeAverageForceCalcFlow: Flow - get() = gradeAverageForceCalcPref.asFlow() - val gradeExpandMode: GradeExpandMode get() = GradeExpandMode.getByValue( getString( @@ -153,24 +141,12 @@ class PreferencesRepository @Inject constructor( R.string.pref_default_grade_modifier_plus ).toDouble() - val gradePlusModifierFlow: Flow - get() = getStringFlow( - R.string.pref_key_grade_modifier_plus, - R.string.pref_default_grade_modifier_plus - ).asFlow().map { it.toDouble() } - val gradeMinusModifier: Double get() = getString( R.string.pref_key_grade_modifier_minus, R.string.pref_default_grade_modifier_minus ).toDouble() - val gradeMinusModifierFlow: Flow - get() = getStringFlow( - R.string.pref_key_grade_modifier_minus, - R.string.pref_default_grade_modifier_minus - ).asFlow().map { it.toDouble() } - val fillMessageContent: Boolean get() = getBoolean( R.string.pref_key_fill_message_content, @@ -199,25 +175,30 @@ class PreferencesRepository @Inject constructor( ) ) - val showTimetableGaps: TimetableGapsMode - get() = TimetableGapsMode.getByValue( - getString( - R.string.pref_key_timetable_show_gaps, - R.string.pref_default_timetable_show_gaps - ) + val showTimetableTimers: Boolean + get() = getBoolean( + R.string.pref_key_timetable_show_timers, + R.bool.pref_default_timetable_show_timers ) + var isHomeworkFullscreen: Boolean + get() = getBoolean( + R.string.pref_key_homework_fullscreen, + R.bool.pref_default_homework_fullscreen + ) + set(value) = sharedPref.edit().putBoolean("homework_fullscreen", value).apply() + val showSubjectsWithoutGrades: Boolean get() = getBoolean( R.string.pref_key_subjects_without_grades, R.bool.pref_default_subjects_without_grades ) - val isOptionalArithmeticAverageFlow: Flow - get() = flowSharedPref.getBoolean( - context.getString(R.string.pref_key_optional_arithmetic_average), - context.resources.getBoolean(R.bool.pref_default_optional_arithmetic_average) - ).asFlow() + val isOptionalArithmeticAverage: Boolean + get() = getBoolean( + R.string.pref_key_optional_arithmetic_average, + R.bool.pref_default_optional_arithmetic_average + ) var lasSyncDate: Instant? get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date) @@ -308,6 +289,19 @@ class PreferencesRepository @Inject constructor( get() = sharedPref.getBoolean(PREF_KEY_APP_SUPPORT_SHOWN, false) set(value) = sharedPref.edit { putBoolean(PREF_KEY_APP_SUPPORT_SHOWN, value) } + var isAgreeToProcessData: Boolean + get() = getBoolean( + R.string.pref_key_ads_consent_data_processing, + R.bool.pref_default_ads_consent_data_processing + ) + set(value) = sharedPref.edit { + putBoolean(context.getString(R.string.pref_key_ads_consent_data_processing), value) + } + + var isPersonalizedAdsEnabled: Boolean + get() = sharedPref.getBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, false) + set(value) = sharedPref.edit { putBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, value) } + val isAdsEnabledFlow = flowSharedPref.getBoolean( context.getString(R.string.pref_key_ads_enabled), context.resources.getBoolean(R.bool.pref_default_ads_enabled) @@ -322,56 +316,11 @@ class PreferencesRepository @Inject constructor( putBoolean(context.getString(R.string.pref_key_ads_enabled), value) } - var appMenuItemOrder: List - get() { - val value = sharedPref.getString(PREF_KEY_APP_MENU_ITEM_ORDER, null) - ?: return AppMenuItem.defaultAppMenuItemList - - return json.decodeFromString(value) - } - set(value) = sharedPref.edit { - putString( - PREF_KEY_APP_MENU_ITEM_ORDER, - json.encodeToString(value) - ) - } - - var isIncognitoMode: Boolean - get() = getBoolean(R.string.pref_key_incognito_moge, R.bool.pref_default_incognito_mode) - set(value) = sharedPref.edit { - putBoolean(context.getString(R.string.pref_key_incognito_moge), value) - } - - var installationId: String - get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty() - private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) } - - init { - if (installationId.isEmpty()) { - installationId = UUID.randomUUID().toString() - } - } - private fun getLong(id: Int, default: Int) = getLong(context.getString(id), default) private fun getLong(id: String, default: Int) = sharedPref.getLong(id, context.resources.getString(default).toLong()) - private fun getStringFlow(id: Int, default: Int) = - flowSharedPref.getString(context.getString(id), context.getString(default)) - - private fun getObjectFlow( - @StringRes id: Int, - @StringRes default: Int, - serializer: Serializer - ): Preference = flowSharedPref.getObject( - key = context.getString(id), - serializer = serializer, - defaultValue = serializer.deserialize( - flowSharedPref.getString(context.getString(default)).get() - ) - ) - private fun getString(id: Int, default: Int) = getString(context.getString(id), default) private fun getString(id: String, default: Int) = @@ -382,14 +331,23 @@ 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_APP_MENU_ITEM_ORDER = "app_menu_item_order" - private const val PREF_KEY_INSTALLATION_ID = "installation_id" + private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position" + private const val PREF_KEY_IN_APP_REVIEW_COUNT = "in_app_review_count" + private const val PREF_KEY_IN_APP_REVIEW_DATE = "in_app_review_date" + private const val PREF_KEY_IN_APP_REVIEW_DONE = "in_app_review_done" + private const val PREF_KEY_APP_SUPPORT_SHOWN = "app_support_shown" + + private const val PREF_KEY_PERSONALIZED_ADS_ENABLED = "personalized_ads_enabled" + private const val PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS = "admin_message_dismissed_ids" } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt index 79984ce6d..e80f028e1 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 @@ -33,11 +33,9 @@ class RecipientRepository @Inject constructor( suspend fun getRecipients( student: Student, - mailbox: Mailbox?, - type: MailboxType, + mailbox: Mailbox, + type: MailboxType ): List { - mailbox ?: return emptyList() - val cached = recipientDb.loadAll(type, mailbox.globalKey) val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student)) @@ -49,15 +47,11 @@ class RecipientRepository @Inject constructor( suspend fun getMessageSender( student: Student, - mailbox: Mailbox?, - message: Message, - ): List { - mailbox ?: return emptyList() - - return sdk.init(student) - .getMessageReplayDetails(message.messageGlobalKey) - .sender - .let(::listOf) - .mapToEntities(mailbox.globalKey) - } + mailbox: Mailbox, + message: Message + ): List = sdk.init(student) + .getMessageReplayDetails(message.messageGlobalKey) + .sender + .let(::listOf) + .mapToEntities(mailbox.globalKey) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolsRepository.kt deleted file mode 100644 index 9c6429343..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolsRepository.kt +++ /dev/null @@ -1,68 +0,0 @@ -package io.github.wulkanowy.data.repositories - -import io.github.wulkanowy.data.api.SchoolsService -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.data.pojos.IntegrityRequest -import io.github.wulkanowy.data.pojos.LoginEvent -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.ui.modules.login.LoginData -import io.github.wulkanowy.utils.IntegrityHelper -import io.github.wulkanowy.utils.getCurrentOrLast -import io.github.wulkanowy.utils.init -import kotlinx.coroutines.withTimeout -import timber.log.Timber -import java.util.UUID -import javax.inject.Inject -import javax.inject.Singleton -import kotlin.time.Duration.Companion.seconds - -@Singleton -class SchoolsRepository @Inject constructor( - private val integrityHelper: IntegrityHelper, - private val schoolsService: SchoolsService, - private val sdk: Sdk, -) { - - suspend fun logSchoolLogin(loginData: LoginData, students: List) { - students.forEach { - runCatching { - withTimeout(10.seconds) { - logLogin(loginData, it.student, it.semesters.getCurrentOrLast()) - } - } - .onFailure { Timber.e(it) } - } - } - - private suspend fun logLogin(loginData: LoginData, student: Student, semester: Semester) { - val requestId = UUID.randomUUID().toString() - val token = integrityHelper.getIntegrityToken(requestId) ?: return - - val schoolInfo = sdk - .init(student.copy(password = loginData.password)) - .switchDiary( - diaryId = semester.diaryId, - kindergartenDiaryId = semester.kindergartenDiaryId, - schoolYear = semester.schoolYear - ) - .getSchool() - - schoolsService.logLoginEvent( - IntegrityRequest( - tokenString = token, - data = LoginEvent( - uuid = requestId, - schoolAddress = schoolInfo.address, - schoolName = schoolInfo.name, - schoolShort = student.schoolShortName, - scraperBaseUrl = student.scrapperBaseUrl, - loginType = student.loginType, - symbol = student.symbol, - schoolId = student.schoolSymbol, - ) - ) - ) - } -} 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 dd44df70f..96f019223 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 @@ -40,8 +40,8 @@ class SemesterRepository @Inject constructor( val isNoSemesters = semesters.isEmpty() val isRefreshOnModeChangeRequired = when { - Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE -> { - semesters.firstOrNull { it.isCurrent() }?.let { + Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> { + semesters.firstOrNull { it.isCurrent }?.let { 0 == it.diaryId && 0 == it.kindergartenDiaryId } == true } @@ -49,7 +49,7 @@ class SemesterRepository @Inject constructor( } val isRefreshOnNoCurrentAppropriate = - refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent() } + refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent } return forceRefresh || isNoSemesters || isRefreshOnModeChangeRequired || isRefreshOnNoCurrentAppropriate } 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 bfad12a8f..f006b7d28 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,89 +1,83 @@ package io.github.wulkanowy.data.repositories +import android.content.Context import androidx.room.withTransaction +import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.dao.SemesterDao import io.github.wulkanowy.data.db.dao.StudentDao -import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.StudentName import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.exceptions.NoCurrentStudentException -import io.github.wulkanowy.data.mappers.mapToPojo -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.DispatchersProvider -import io.github.wulkanowy.utils.init -import io.github.wulkanowy.utils.security.Scrambler +import io.github.wulkanowy.utils.security.decrypt +import io.github.wulkanowy.utils.security.encrypt import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Singleton @Singleton class StudentRepository @Inject constructor( + @ApplicationContext private val context: Context, private val dispatchers: DispatchersProvider, private val studentDb: StudentDao, private val semesterDb: SemesterDao, private val sdk: Sdk, - private val appDatabase: AppDatabase, - private val scrambler: Scrambler, + private val appInfo: AppInfo, + private val appDatabase: AppDatabase ) { + suspend fun isStudentSaved() = getSavedStudents(false).isNotEmpty() + suspend fun isCurrentStudentSet() = studentDb.loadCurrent()?.isCurrent ?: false suspend fun getStudentsApi( pin: String, symbol: String, token: String - ): RegisterUser = sdk - .getStudentsFromHebe(token, pin, symbol, "") - .mapToPojo(null) + ): List = + sdk.getStudentsFromMobileApi(token, pin, symbol, "") + .mapToEntities(colors = appInfo.defaultColorsForAvatar) - suspend fun getUserSubjectsFromScrapper( + suspend fun getStudentsScrapper( email: String, password: String, scrapperBaseUrl: String, - domainSuffix: String, symbol: String - ): RegisterUser = sdk - .getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, domainSuffix, symbol) - .mapToPojo(password) + ): List = + sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol) + .mapToEntities(password, appInfo.defaultColorsForAvatar) suspend fun getStudentsHybrid( email: String, password: String, scrapperBaseUrl: String, symbol: String - ): RegisterUser = sdk - .getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol) - .mapToPojo(password) + ): List = + sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol) + .mapToEntities(password, appInfo.defaultColorsForAvatar) - suspend fun getSavedStudents(decryptPass: Boolean = true): List { - return studentDb.loadStudentsWithSemesters().map { (student, semesters) -> - StudentWithSemesters( - student = student.apply { - if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) { + suspend fun getSavedStudents(decryptPass: Boolean = true) = + studentDb.loadStudentsWithSemesters() + .map { + it.apply { + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { student.password = withContext(dispatchers.io) { - scrambler.decrypt(student.password) + decrypt(student.password) } } - }, - semesters = semesters, - ) - } - } + } + } - suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true): StudentWithSemesters? = - studentDb.loadStudentWithSemestersById(id).let { res -> - StudentWithSemesters( - student = res.keys.firstOrNull() ?: return null, - semesters = res.values.first(), - ) - }.apply { - if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) { + suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true) = + studentDb.loadStudentWithSemestersById(id)?.apply { + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { student.password = withContext(dispatchers.io) { - scrambler.decrypt(student.password) + decrypt(student.password) } } } @@ -91,9 +85,9 @@ class StudentRepository @Inject constructor( suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student { val student = studentDb.loadById(id) ?: throw NoCurrentStudentException() - if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) { + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { student.password = withContext(dispatchers.io) { - scrambler.decrypt(student.password) + decrypt(student.password) } } return student @@ -102,9 +96,9 @@ class StudentRepository @Inject constructor( suspend fun getCurrentStudent(decryptPass: Boolean = true): Student { val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException() - if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) { + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { student.password = withContext(dispatchers.io) { - scrambler.decrypt(student.password) + decrypt(student.password) } } return student @@ -115,9 +109,9 @@ class StudentRepository @Inject constructor( val students = studentsWithSemesters.map { it.student } .map { it.apply { - if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.HEBE) { + if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) { password = withContext(dispatchers.io) { - scrambler.encrypt(password) + encrypt(password, context) } } } @@ -146,32 +140,4 @@ class StudentRepository @Inject constructor( suspend fun isOneUniqueStudent() = getSavedStudents(false) .distinctBy { it.student.studentName }.size == 1 - - suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) = - sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .authorizePermission(pesel) - - suspend fun refreshStudentName(student: Student, semester: Semester) { - val newCurrentApiStudent = sdk.init(student) - .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getCurrentStudent() ?: return - - val studentName = StudentName( - studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}" - ).apply { id = student.id } - - studentDb.update(studentName) - } - - suspend fun deleteStudentsAssociatedWithAccount(student: Student) { - studentDb.deleteByEmailAndUserName(student.email, student.userName) - } - - suspend fun clearAll() { - withContext(dispatchers.io) { - scrambler.clearKeyPair() - appDatabase.clearAllTables() - } - } } 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 4e3b40f96..acd71e1f4 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 @@ -40,7 +40,7 @@ class TeacherRepository @Inject constructor( fetch = { sdk.init(student) .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getTeachers() + .getTeachers(semester.semesterId) .mapToEntities(semester) }, saveFetchResult = { old, new -> 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 136fb8d5b..3145c2a23 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 @@ -13,7 +13,6 @@ import io.github.wulkanowy.utils.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.sync.Mutex -import java.time.Instant import java.time.LocalDate import javax.inject.Inject import javax.inject.Singleton @@ -66,7 +65,7 @@ class TimetableRepository @Inject constructor( fetch = { val timetableFull = sdk.init(student) .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear) - .getTimetable(start.monday, end.sunday) + .getTimetableFull(start.monday, end.sunday) timetableFull.mapToEntities(semester) }, @@ -165,11 +164,6 @@ class TimetableRepository @Inject constructor( timetableHeaderDb.insertAll(new uniqueSubtract old) } - fun getLastRefreshTimestamp(semester: Semester, start: LocalDate, end: LocalDate): Instant { - val refreshKey = getRefreshKey(cacheKey, semester, start, end) - return refreshHelper.getLastRefreshTimestamp(refreshKey) - } - suspend fun saveAdditionalList(additionalList: List) = timetableAdditionalDb.insertAll(additionalList) diff --git a/app/src/main/java/io/github/wulkanowy/domain/adminmessage/GetAppropriateAdminMessageUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/adminmessage/GetAppropriateAdminMessageUseCase.kt deleted file mode 100644 index b55bf899d..000000000 --- a/app/src/main/java/io/github/wulkanowy/domain/adminmessage/GetAppropriateAdminMessageUseCase.kt +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.wulkanowy.domain.adminmessage - -import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.db.entities.AdminMessage -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.enums.MessageType -import io.github.wulkanowy.data.mapResourceData -import io.github.wulkanowy.data.repositories.AdminMessageRepository -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.utils.AppInfo -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject - -class GetAppropriateAdminMessageUseCase @Inject constructor( - private val adminMessageRepository: AdminMessageRepository, - private val preferencesRepository: PreferencesRepository, - private val appInfo: AppInfo -) { - - operator fun invoke(student: Student, type: MessageType): Flow> { - return invoke(student.scrapperBaseUrl, type) - } - - operator fun invoke(scrapperBaseUrl: String, type: MessageType): Flow> { - return adminMessageRepository.getAdminMessages().mapResourceData { adminMessages -> - adminMessages - .asSequence() - .filter { it.isNotDismissed() } - .filter { it.isVersionMatch() } - .filter { it.isRegisterHostMatch(scrapperBaseUrl) } - .filter { it.isFlavorMatch() } - .filter { it.isTypeMatch(type) } - .maxByOrNull { it.id } - } - } - - private fun AdminMessage.isNotDismissed(): Boolean { - return id !in preferencesRepository.dismissedAdminMessageIds - } - - private fun AdminMessage.isRegisterHostMatch(scrapperBaseUrl: String): Boolean { - return targetRegisterHost?.let { - scrapperBaseUrl.contains(it, true) - } ?: true - } - - private fun AdminMessage.isFlavorMatch(): Boolean { - return targetFlavor?.equals(appInfo.buildFlavor, true) ?: true - } - - private fun AdminMessage.isVersionMatch(): Boolean { - val isCorrectMaxVersion = versionMax?.let { it >= appInfo.versionCode } ?: true - val isCorrectMinVersion = versionMin?.let { it <= appInfo.versionCode } ?: true - - return isCorrectMaxVersion && isCorrectMinVersion - } - - private fun AdminMessage.isTypeMatch(messageType: MessageType): Boolean { - if (messageType in types) return true - if (MessageType.GENERAL_MESSAGE in types) return true - - return false - } -} diff --git a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt deleted file mode 100644 index 669514aae..000000000 --- a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt +++ /dev/null @@ -1,65 +0,0 @@ -package io.github.wulkanowy.domain.messages - -import io.github.wulkanowy.data.db.dao.MailboxDao -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.data.db.entities.Student -import javax.inject.Inject - -class GetMailboxByStudentUseCase @Inject constructor( - private val mailboxDao: MailboxDao, -) { - - suspend operator fun invoke(student: Student): Mailbox? { - return mailboxDao.loadAll(student.email) - .filterByStudent(student) - } - - private fun List.filterByStudent(student: Student): Mailbox? { - val normalizedStudentName = student.studentName.normalizeStudentName() - - return singleOrNull { - it.studentName.normalizeStudentName() == normalizedStudentName - } ?: singleOrNull { - it.studentName.normalizeStudentName() == normalizedStudentName - && it.schoolNameShort == student.schoolShortName - } ?: singleOrNull { - it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart() - } ?: singleOrNull { - it.studentName.getReversedName() == normalizedStudentName - } ?: singleOrNull { - it.studentName.getUnauthorizedVersion() == normalizedStudentName - } - } - - private fun String.normalizeStudentName(): String { - return trim().split(" ") - .filter { it.isNotBlank() } - .joinToString(" ") { part -> - part.lowercase().replaceFirstChar { it.uppercase() } - } - } - - private fun String.getFirstAndLastPart(): String { - val parts = normalizeStudentName().split(" ") - - val endParts = parts.filterIndexed { i, _ -> - i == 0 || parts.size - 1 == i - } - return endParts.joinToString(" ") - } - - private fun String.getReversedName(): String { - val parts = normalizeStudentName().split(" ") - - return parts - .asReversed() - .joinToString(" ") - } - - private fun String.getUnauthorizedVersion(): String { - return normalizeStudentName().split(" ") - .joinToString(" ") { - it.first() + "*".repeat(it.length - 1) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt index e0a136f98..c1bed4dd3 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt @@ -4,12 +4,18 @@ import android.os.Build.VERSION.SDK_INT import android.os.Build.VERSION_CODES.O import androidx.core.app.NotificationManagerCompat import androidx.lifecycle.asFlow -import androidx.work.* import androidx.work.BackoffPolicy.EXPONENTIAL +import androidx.work.Constraints +import androidx.work.Data import androidx.work.ExistingPeriodicWorkPolicy.KEEP -import androidx.work.ExistingPeriodicWorkPolicy.UPDATE +import androidx.work.ExistingPeriodicWorkPolicy.REPLACE +import androidx.work.ExistingWorkPolicy import androidx.work.NetworkType.CONNECTED import androidx.work.NetworkType.UNMETERED +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkInfo +import androidx.work.WorkManager import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY import io.github.wulkanowy.data.repositories.PreferencesRepository @@ -54,7 +60,7 @@ class SyncManager @Inject constructor( val serviceInterval = preferencesRepository.servicesInterval workManager.enqueueUniquePeriodicWork( - SyncWorker::class.java.simpleName, if (restart) UPDATE else KEEP, + SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP, PeriodicWorkRequestBuilder(serviceInterval, MINUTES) .setInitialDelay(10, MINUTES) .setBackoffCriteria(EXPONENTIAL, 30, MINUTES) 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 index 99473a8ec..49842c9a6 100644 --- 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 @@ -8,6 +8,7 @@ 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 @@ -21,9 +22,8 @@ class NewAttendanceNotification @Inject constructor( suspend fun notify(items: List, student: Student) { val lines = items.filterNot { it.presence || it.name == "UNKNOWN" } .map { - val lesson = it.subject.ifBlank { "Lekcja ${it.number}" } val description = context.getString(it.descriptionRes) - "${it.date.toFormattedString("dd.MM")} - $lesson: $description" + "${it.date.toFormattedString("dd.MM")} - ${it.subject}: $description" } .ifEmpty { return } 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 45523d51e..3b7bcff05 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 @@ -8,6 +8,7 @@ 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 javax.inject.Inject diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt index c7824e61f..180568267 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt @@ -3,6 +3,7 @@ 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.enums.MessageFolder.RECEIVED +import io.github.wulkanowy.data.repositories.MailboxRepository import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.waitForResult import io.github.wulkanowy.services.sync.notifications.NewMessageNotification @@ -11,11 +12,12 @@ import javax.inject.Inject class MessageWork @Inject constructor( private val messageRepository: MessageRepository, + private val mailboxRepository: MailboxRepository, private val newMessageNotification: NewMessageNotification, ) : Work { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { - val mailbox = messageRepository.getMailboxByStudent(student) + val mailbox = mailboxRepository.getMailbox(student) messageRepository.getMessages( student = student, mailbox = mailbox, @@ -24,7 +26,7 @@ class MessageWork @Inject constructor( notify = notify ).waitForResult() - messageRepository.getMessagesFromDatabase(student, mailbox).first() + messageRepository.getMessagesFromDatabase(mailbox).first() .filter { !it.isNotified && it.unread }.let { if (it.isNotEmpty()) newMessageNotification.notify(it, student) messageRepository.updateMessages(it.onEach { message -> message.isNotified = true }) diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt index 90b20651d..b1322ada3 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt @@ -1,23 +1,22 @@ package io.github.wulkanowy.services.sync.works -import io.github.wulkanowy.data.dataOrNull import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.MessageRepository +import io.github.wulkanowy.data.repositories.MailboxRepository import io.github.wulkanowy.data.repositories.RecipientRepository -import io.github.wulkanowy.data.toFirstResult import javax.inject.Inject class RecipientWork @Inject constructor( - private val messageRepository: MessageRepository, + private val mailboxRepository: MailboxRepository, private val recipientRepository: RecipientRepository ) : Work { override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) { - val mailboxes = messageRepository.getMailboxes(student, forceRefresh = true).toFirstResult() - mailboxes.dataOrNull?.forEach { - recipientRepository.refreshRecipients(student, it, MailboxType.EMPLOYEE) - } + mailboxRepository.refreshMailboxes(student) + + val mailbox = mailboxRepository.getMailbox(student) + + recipientRepository.refreshRecipients(student, mailbox, MailboxType.EMPLOYEE) } } diff --git a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt index ffdb07ecd..45cd2b04e 100644 --- a/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt +++ b/app/src/main/java/io/github/wulkanowy/services/widgets/TimetableWidgetService.kt @@ -25,21 +25,13 @@ class TimetableWidgetService : RemoteViewsService() { lateinit var semesterRepo: SemesterRepository @Inject - lateinit var sharedPref: SharedPrefProvider + lateinit var prefRepository: PreferencesRepository @Inject - lateinit var prefRepository: PreferencesRepository + lateinit var sharedPref: SharedPrefProvider override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory { Timber.d("TimetableWidgetFactory created") - return TimetableWidgetFactory( - timetableRepository = timetableRepo, - studentRepository = studentRepo, - semesterRepository = semesterRepo, - sharedPref = sharedPref, - prefRepository = prefRepository, - context = applicationContext, - intent = intent, - ) + return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, prefRepository, sharedPref, applicationContext, intent) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt index 026d38ded..075557a5c 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 @@ -4,13 +4,12 @@ import android.app.ActivityManager import android.os.Bundle import android.view.View import android.widget.Toast +import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.viewbinding.ViewBinding -import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar.LENGTH_LONG import io.github.wulkanowy.R -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.utils.FragmentLifecycleLogger import io.github.wulkanowy.utils.getThemeAttrColor @@ -31,8 +30,6 @@ abstract class BaseActivity, VB : ViewBinding> : protected var messageContainer: View? = null - protected var messageAnchor: View? = null - abstract var presenter: T override fun onCreate(savedInstanceState: Bundle?) { @@ -51,7 +48,6 @@ abstract class BaseActivity, VB : ViewBinding> : if (messageContainer != null) { Snackbar.make(messageContainer!!, text, LENGTH_LONG) .setAction(R.string.all_details) { showErrorDetailsDialog(error) } - .apply { messageAnchor?.let { anchorView = it } } .show() } else showMessage(text) } @@ -61,40 +57,23 @@ abstract class BaseActivity, VB : ViewBinding> : } override fun showMessage(text: String) { - if (messageContainer != null) { - Snackbar.make(messageContainer!!, text, LENGTH_LONG) - .apply { messageAnchor?.let { anchorView = it } } - .show() - } else Toast.makeText(this, text, Toast.LENGTH_LONG).show() + if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show() + else Toast.makeText(this, text, Toast.LENGTH_LONG).show() } - override fun showExpiredCredentialsDialog() { - MaterialAlertDialogBuilder(this) - .setTitle(R.string.main_expired_credentials_title) - .setMessage(R.string.main_expired_credentials_description) - .setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onConfirmExpiredCredentialsSelected() } - .setNegativeButton(android.R.string.cancel) { _, _ -> } - .show() - } - - override fun showDecryptionFailedDialog() { - MaterialAlertDialogBuilder(this) + override fun showExpiredDialog() { + AlertDialog.Builder(this) .setTitle(R.string.main_session_expired) .setMessage(R.string.main_session_relogin) - .setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onConfirmDecryptionFailedSelected() } + .setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onExpiredLoginSelected() } .setNegativeButton(android.R.string.cancel) { _, _ -> } .show() } - override fun showAuthDialog() { - AuthDialog.newInstance().show(supportFragmentManager, "auth_dialog") - } - override fun showChangePasswordSnackbar(redirectUrl: String) { messageContainer?.let { Snackbar.make(it, R.string.error_password_change_required, LENGTH_LONG) .setAction(R.string.all_change) { openInternetBrowser(redirectUrl) } - .apply { messageAnchor?.let { anchorView = it } } .show() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt index 50e4b05d4..25a53395d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt @@ -1,14 +1,8 @@ package io.github.wulkanowy.ui.base -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup import android.widget.Toast import androidx.fragment.app.DialogFragment import androidx.viewbinding.ViewBinding -import com.google.android.material.elevation.SurfaceColors -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.lifecycleAwareVariable import javax.inject.Inject @@ -28,12 +22,8 @@ abstract class BaseDialogFragment : DialogFragment(), BaseView Toast.makeText(context, text, Toast.LENGTH_LONG).show() } - override fun showExpiredCredentialsDialog() { - (activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog() - } - - override fun showDecryptionFailedDialog() { - (activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog() + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() } override fun openClearLoginView() { @@ -44,25 +34,10 @@ abstract class BaseDialogFragment : DialogFragment(), BaseView (activity as? BaseActivity<*, *>)?.showChangePasswordSnackbar(redirectUrl) } - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") - } - override fun showErrorDetailsDialog(error: Throwable) { ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - view.setBackgroundColor(SurfaceColors.SURFACE_3.getColor(requireContext())) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ) = binding.root - override fun onResume() { super.onResume() analyticsHelper.setCurrentScreen(requireActivity(), this::class.simpleName) diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt index cec2670b2..dbc5af3a9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseFragment.kt @@ -7,7 +7,6 @@ import androidx.viewbinding.ViewBinding import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar.LENGTH_LONG import io.github.wulkanowy.R -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.utils.lifecycleAwareVariable abstract class BaseFragment(@LayoutRes layoutId: Int) : Fragment(layoutId), @@ -39,16 +38,8 @@ abstract class BaseFragment(@LayoutRes layoutId: Int) : Fragme } } - override fun showExpiredCredentialsDialog() { - (activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog() - } - - override fun showDecryptionFailedDialog() { - (activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog() - } - - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() } override fun openClearLoginView() { 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 ee92e4fc1..15c069f54 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt @@ -1,15 +1,10 @@ package io.github.wulkanowy.ui.base import io.github.wulkanowy.data.repositories.StudentRepository -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.launch import timber.log.Timber open class BasePresenter( @@ -28,37 +23,19 @@ open class BasePresenter( this.view = view errorHandler.apply { showErrorMessage = view::showError - onExpiredCredentials = view::showExpiredCredentialsDialog - onDecryptionFailed = view::showDecryptionFailedDialog + onSessionExpired = view::showExpiredDialog onNoCurrentStudent = view::openClearLoginView onPasswordChangeRequired = view::showChangePasswordSnackbar - onAuthorizationRequired = view::showAuthDialog } } - fun onConfirmDecryptionFailedSelected() { - Timber.i("Attempt to clear all data") - - presenterScope.launch { - runCatching { studentRepository.clearAll() } - .onFailure { - Timber.i("Clear data result: An exception occurred") - errorHandler.dispatch(it) - } - .onSuccess { - Timber.i("Clear data result: Open login view") - view?.openClearLoginView() - } - } - } - - fun onConfirmExpiredCredentialsSelected() { - Timber.i("Attempt to delete students associated with the account and switch to new student") + fun onExpiredLoginSelected() { + Timber.i("Attempt to switch the student after the session expires") presenterScope.launch { runCatching { val student = studentRepository.getCurrentStudent(false) - studentRepository.deleteStudentsAssociatedWithAccount(student) + studentRepository.logoutStudent(student) val students = studentRepository.getSavedStudents(false) if (students.isNotEmpty()) { @@ -67,11 +44,11 @@ open class BasePresenter( } } .onFailure { - Timber.i("Delete students result: An exception occurred") + Timber.i("Switch student result: An exception occurred") errorHandler.dispatch(it) } .onSuccess { - Timber.i("Delete students result: Open login view") + Timber.i("Switch student result: Open login view") view?.openClearLoginView() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt index e97a6ab90..d3165ea44 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseView.kt @@ -6,11 +6,7 @@ interface BaseView { fun showMessage(text: String) - fun showExpiredCredentialsDialog() - - fun showDecryptionFailedDialog() - - fun showAuthDialog() + fun showExpiredDialog() fun openClearLoginView() 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 679d904a3..48c003b7e 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 @@ -4,32 +4,27 @@ import android.app.Dialog import android.content.ClipData import android.content.ClipboardManager import android.os.Bundle -import android.view.View +import android.view.LayoutInflater import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.appcompat.app.AlertDialog import androidx.core.content.getSystemService import androidx.core.os.bundleOf import androidx.core.view.isGone +import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.DialogErrorBinding import io.github.wulkanowy.utils.* import javax.inject.Inject @AndroidEntryPoint -class ErrorDialog : BaseDialogFragment() { +class ErrorDialog : DialogFragment() { @Inject lateinit var appInfo: AppInfo - @Inject - lateinit var preferencesRepository: PreferencesRepository - - private lateinit var error: Throwable - companion object { private const val ARGUMENT_KEY = "error" @@ -38,31 +33,32 @@ class ErrorDialog : BaseDialogFragment() { } } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - error = requireArguments().serializable(ARGUMENT_KEY) + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val error = requireArguments().getSerializable(ARGUMENT_KEY) as Throwable + + val binding = DialogErrorBinding.inflate(LayoutInflater.from(context)) + binding.bindErrorDetails(error) + + return getAlertDialog(binding, error).apply { + enableReportButtonIfErrorIsReportable(error) + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + private fun getAlertDialog(binding: DialogErrorBinding, error: Throwable): AlertDialog { return MaterialAlertDialogBuilder(requireContext()).apply { val errorStacktrace = error.stackTraceToString() setTitle(R.string.all_details) - setView(DialogErrorBinding.inflate(layoutInflater).apply { binding = this }.root) + setView(binding.root) setNeutralButton(R.string.about_feedback) { _, _ -> openConfirmDialog { openEmailClient(errorStacktrace) } } setNegativeButton(android.R.string.cancel) { _, _ -> } setPositiveButton(android.R.string.copy) { _, _ -> copyErrorToClipboard(errorStacktrace) } - }.create().apply { - setOnShowListener { - getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported() - } - } + }.create() } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - with(binding) { + private fun DialogErrorBinding.bindErrorDetails(error: Throwable) { + return with(this) { errorDialogHumanizedMessage.text = resources.getErrorString(error) errorDialogErrorMessage.text = error.localizedMessage errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank() @@ -71,6 +67,12 @@ class ErrorDialog : BaseDialogFragment() { } } + private fun AlertDialog.enableReportButtonIfErrorIsReportable(error: Throwable) { + setOnShowListener { + getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported() + } + } + private fun copyErrorToClipboard(errorStacktrace: String) { val clip = ClipData.newPlainText("Error details", errorStacktrace) requireActivity().getSystemService()?.setPrimaryClip(clip) @@ -78,7 +80,7 @@ class ErrorDialog : BaseDialogFragment() { } private fun openConfirmDialog(callback: () -> Unit) { - MaterialAlertDialogBuilder(requireContext()) + AlertDialog.Builder(requireContext()) .setTitle(R.string.dialog_error_check_update) .setMessage(R.string.dialog_error_check_update_message) .setNeutralButton(R.string.about_feedback) { _, _ -> callback() } @@ -97,8 +99,7 @@ class ErrorDialog : BaseDialogFragment() { R.string.about_feedback_template, "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), - "${appInfo.versionName}-${appInfo.buildFlavor}", - preferencesRepository.installationId, + "${appInfo.versionName}-${appInfo.buildFlavor}" ) + "\n" + content, onActivityNotFound = { requireContext().openInternetBrowser( @@ -108,4 +109,8 @@ class ErrorDialog : BaseDialogFragment() { } ) } + + private fun showMessage(text: String) { + Toast.makeText(requireContext(), text, LENGTH_LONG).show() + } } 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 56905709d..afe200e9a 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 @@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.base import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.data.exceptions.NoCurrentStudentException -import io.github.wulkanowy.sdk.scrapper.exception.AuthorizationRequiredException import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException import io.github.wulkanowy.utils.getErrorString @@ -15,16 +14,12 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co var showErrorMessage: (String, Throwable) -> Unit = { _, _ -> } - var onExpiredCredentials: () -> Unit = {} - - var onDecryptionFailed: () -> Unit = {} + var onSessionExpired: () -> Unit = {} var onNoCurrentStudent: () -> Unit = {} var onPasswordChangeRequired: (String) -> Unit = {} - var onAuthorizationRequired: () -> Unit = {} - fun dispatch(error: Throwable) { Timber.e(error, "An exception occurred while the Wulkanowy was running") proceed(error) @@ -34,19 +29,15 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co showErrorMessage(context.resources.getErrorString(error), error) when (error) { is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl) - is ScramblerException -> onDecryptionFailed() - is BadCredentialsException -> onExpiredCredentials() + is ScramblerException, is BadCredentialsException -> onSessionExpired() is NoCurrentStudentException -> onNoCurrentStudent() - is AuthorizationRequiredException -> onAuthorizationRequired() } } open fun clear() { showErrorMessage = { _, _ -> } - onExpiredCredentials = {} - onDecryptionFailed = {} + onSessionExpired = {} onNoCurrentStudent = {} onPasswordChangeRequired = {} - onAuthorizationRequired = {} } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt index f42f315ce..2d83bbbf9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ThemeManager.kt @@ -1,19 +1,17 @@ package io.github.wulkanowy.ui.base -import android.content.pm.PackageInfo -import android.content.pm.PackageManager import android.content.pm.PackageManager.GET_ACTIVITIES -import android.os.Build import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate -import com.google.android.material.color.DynamicColors +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO +import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES import io.github.wulkanowy.R import io.github.wulkanowy.data.enums.AppTheme import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity +import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import javax.inject.Inject import javax.inject.Singleton @@ -27,40 +25,31 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer when (activity) { is MainActivity -> activity.setTheme(R.style.WulkanowyTheme_Black) is LoginActivity -> activity.setTheme(R.style.WulkanowyTheme_Login_Black) + is SendMessageActivity -> activity.setTheme(R.style.WulkanowyTheme_MessageSend_Black) } } - } else if (activity is TimetableWidgetConfigureActivity || activity is LuckyNumberWidgetConfigureActivity) { - DynamicColors.applyToActivityIfAvailable(activity) } } fun applyDefaultTheme() { AppCompatDelegate.setDefaultNightMode( when (preferencesRepository.appTheme) { - AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO - AppTheme.DARK, AppTheme.BLACK -> AppCompatDelegate.MODE_NIGHT_YES - AppTheme.SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + AppTheme.LIGHT -> MODE_NIGHT_NO + AppTheme.DARK, AppTheme.BLACK -> MODE_NIGHT_YES + AppTheme.SYSTEM -> MODE_NIGHT_FOLLOW_SYSTEM } ) } - private fun isThemeApplicable(activity: AppCompatActivity): Boolean = - getPackageInfo(activity) + private fun isThemeApplicable(activity: AppCompatActivity) = + activity.packageManager + .getPackageInfo(activity.packageName, GET_ACTIVITIES) .activities .singleOrNull { it.name == activity::class.java.canonicalName } ?.theme .let { it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar || it == R.style.WulkanowyTheme_Login || it == R.style.WulkanowyTheme_Login_Black + || it == R.style.WulkanowyTheme_MessageSend || it == R.style.WulkanowyTheme_MessageSend_Black } - - @Suppress("DEPRECATION") - private fun getPackageInfo(activity: AppCompatActivity): PackageInfo { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - activity.packageManager.getPackageInfo( - activity.packageName, - PackageManager.PackageInfoFlags.of(GET_ACTIVITIES.toLong()) - ) - } else activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES) - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt index f0969fac4..561419a05 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt @@ -9,14 +9,11 @@ import io.github.wulkanowy.ui.modules.exam.ExamFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.homework.HomeworkFragment import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment -import io.github.wulkanowy.ui.modules.luckynumber.history.LuckyNumberHistoryFragment import io.github.wulkanowy.ui.modules.message.MessageFragment -import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment import io.github.wulkanowy.ui.modules.more.MoreFragment import io.github.wulkanowy.ui.modules.note.NoteFragment -import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment +import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment -import io.github.wulkanowy.ui.modules.settings.SettingsFragment import io.github.wulkanowy.ui.modules.timetable.TimetableFragment import kotlinx.serialization.Serializable import java.time.LocalDate @@ -42,13 +39,10 @@ sealed class Destination { NOTE(Note), CONFERENCE(Conference), SCHOOL_ANNOUNCEMENT(SchoolAnnouncement), - SCHOOL_AND_TEACHERS(SchoolAndTeachers), - LUCKY_NUMBER(LuckyNumber), - LUCKY_NUMBER_HISTORY(LuckyNumberHistory), + SCHOOL(School), + LUCKY_NUMBER(More), MORE(More), - MESSAGE(Message), - MOBILE_DEVICE(MobileDevice), - SETTINGS(Settings); + MESSAGE(Message); } @Serializable @@ -109,9 +103,9 @@ sealed class Destination { } @Serializable - object SchoolAndTeachers : Destination() { - override val destinationType get() = Type.SCHOOL_AND_TEACHERS - override val destinationFragment get() = SchoolAndTeachersFragment.newInstance() + object School : Destination() { + override val destinationType get() = Type.SCHOOL + override val destinationFragment get() = SchoolFragment.newInstance() } @Serializable @@ -120,12 +114,6 @@ sealed class Destination { override val destinationFragment get() = LuckyNumberFragment.newInstance() } - @Serializable - object LuckyNumberHistory : Destination() { - override val destinationType get() = Type.LUCKY_NUMBER_HISTORY - override val destinationFragment get() = LuckyNumberHistoryFragment.newInstance() - } - @Serializable object More : Destination() { override val destinationType get() = Type.MORE @@ -137,16 +125,4 @@ sealed class Destination { override val destinationType get() = Type.MESSAGE override val destinationFragment get() = MessageFragment.newInstance() } - - @Serializable - object MobileDevice : Destination() { - override val destinationType get() = Type.MOBILE_DEVICE - override val destinationFragment get() = MobileDeviceFragment.newInstance() - } - - @Serializable - object Settings : Destination() { - override val destinationType get() = Type.SETTINGS - override val destinationFragment get() = SettingsFragment.newInstance() - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt index d7f39e303..701656b55 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt @@ -6,7 +6,6 @@ import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.FragmentAboutBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment @@ -31,9 +30,6 @@ class AboutFragment : BaseFragment(R.layout.fragment_about @Inject lateinit var appInfo: AppInfo - @Inject - lateinit var preferencesRepository: PreferencesRepository - override val versionRes: Triple? get() = context?.run { val buildTimestamp = @@ -189,8 +185,7 @@ class AboutFragment : BaseFragment(R.layout.fragment_about R.string.about_feedback_template, "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), - "${appInfo.versionName}-${appInfo.buildFlavor}", - preferencesRepository.installationId, + "${appInfo.versionName}-${appInfo.buildFlavor}" ), onActivityNotFound = { requireContext().openInternetBrowser( diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt index f115372a5..051c93c95 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt @@ -34,7 +34,6 @@ class AccountFragment : BaseFragment(R.layout.fragment_a override val titleStringId = R.string.account_title - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) 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 d6bc6154b..c3137ec58 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt @@ -6,10 +6,8 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import androidx.appcompat.app.AlertDialog -import androidx.core.os.bundleOf import androidx.core.view.get import androidx.core.view.isVisible -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Student @@ -23,7 +21,6 @@ import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView import io.github.wulkanowy.utils.createNameInitialsDrawable import io.github.wulkanowy.utils.nickOrName -import io.github.wulkanowy.utils.serializable import javax.inject.Inject @AndroidEntryPoint @@ -40,12 +37,12 @@ class AccountDetailsFragment : private const val ARGUMENT_KEY = "Data" - fun newInstance(student: Student) = AccountDetailsFragment().apply { - arguments = bundleOf(ARGUMENT_KEY to student) - } + fun newInstance(student: Student) = + AccountDetailsFragment().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, student) } + } } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -54,7 +51,7 @@ class AccountDetailsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentAccountDetailsBinding.bind(view) - presenter.onAttachView(this, requireArguments().serializable(ARGUMENT_KEY)) + presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student) } override fun initView() { @@ -115,7 +112,7 @@ class AccountDetailsFragment : override fun showLogoutConfirmDialog() { context?.let { - MaterialAlertDialogBuilder(it) + AlertDialog.Builder(it) .setTitle(R.string.account_logout_student) .setMessage(R.string.account_confirm) .setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt index 4229579c0..21a7a492d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditDialog.kt @@ -1,16 +1,14 @@ package io.github.wulkanowy.ui.modules.account.accountedit -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf +import android.view.ViewGroup import androidx.recyclerview.widget.GridLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.databinding.DialogAccountEditBinding import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.serializable import javax.inject.Inject @AndroidEntryPoint @@ -26,21 +24,28 @@ class AccountEditDialog : BaseDialogFragment(), Accoun private const val ARGUMENT_KEY = "student_with_semesters" - fun newInstance(student: Student) = AccountEditDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to student) - } + fun newInstance(student: Student) = + AccountEditDialog().apply { + arguments = Bundle().apply { + putSerializable(ARGUMENT_KEY, student) + } + } } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogAccountEditBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View = DialogAccountEditBinding.inflate(inflater).apply { binding = this }.root + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - presenter.onAttachView(this, requireArguments().serializable(ARGUMENT_KEY)) + presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student) } override fun initView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt index 2d2dccec4..4279102e1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt @@ -1,11 +1,10 @@ package io.github.wulkanowy.ui.modules.account.accountquick -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf +import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.DialogAccountQuickBinding @@ -14,7 +13,6 @@ import io.github.wulkanowy.ui.modules.account.AccountAdapter import io.github.wulkanowy.ui.modules.account.AccountFragment import io.github.wulkanowy.ui.modules.account.AccountItem import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.utils.serializable import javax.inject.Inject @AndroidEntryPoint @@ -32,23 +30,27 @@ class AccountQuickDialog : BaseDialogFragment(), Acco fun newInstance(studentsWithSemesters: List) = AccountQuickDialog().apply { - arguments = bundleOf(STUDENTS_ARGUMENT_KEY to studentsWithSemesters.toTypedArray()) + arguments = Bundle().apply { + putSerializable(STUDENTS_ARGUMENT_KEY, studentsWithSemesters.toTypedArray()) + } } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView( - DialogAccountQuickBinding.inflate(layoutInflater) - .apply { binding = this }.root - ) - .create() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogAccountQuickBinding.inflate(inflater).apply { binding = this }.root + + @Suppress("UNCHECKED_CAST") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - val studentsWithSemesters = requireArguments() - .serializable>(STUDENTS_ARGUMENT_KEY).toList() + val studentsWithSemesters = + (requireArguments()[STUDENTS_ARGUMENT_KEY] as Array).toList() presenter.onAttachView(this, studentsWithSemesters) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt index c0026bee5..9b5c63e4c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceDialog.kt @@ -1,20 +1,19 @@ package io.github.wulkanowy.ui.modules.attendance -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.databinding.DialogAttendanceBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment import io.github.wulkanowy.utils.descriptionRes -import io.github.wulkanowy.utils.serializable +import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.toFormattedString -@AndroidEntryPoint -class AttendanceDialog : BaseDialogFragment() { +class AttendanceDialog : DialogFragment() { + + private var binding: DialogAttendanceBinding by lifecycleAwareVariable() private lateinit var attendance: Attendance @@ -23,20 +22,23 @@ class AttendanceDialog : BaseDialogFragment() { private const val ARGUMENT_KEY = "Item" fun newInstance(exam: Attendance) = AttendanceDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to exam) + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - attendance = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + attendance = getSerializable(ARGUMENT_KEY) as Attendance + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogAttendanceBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogAttendanceBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) 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 6e842b4d7..6354b5e04 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 @@ -4,10 +4,10 @@ import android.content.DialogInterface.BUTTON_POSITIVE import android.os.Bundle import android.view.* import android.view.View.* +import androidx.appcompat.app.AlertDialog import androidx.appcompat.view.ActionMode import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Attendance @@ -84,7 +84,6 @@ class AttendanceFragment : BaseFragment(R.layout.frag } } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -124,7 +123,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() } - attendanceNavContainer.elevation = requireContext().dpToPx(3f) + attendanceNavContainer.elevation = requireContext().dpToPx(8f) } } @@ -148,10 +147,6 @@ class AttendanceFragment : BaseFragment(R.layout.frag binding.attendanceNavDate.text = date } - override fun showNavigation(show: Boolean) { - binding.attendanceNavContainer.isVisible = show - } - override fun clearData() { with(attendanceAdapter) { items = emptyList() @@ -232,7 +227,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag override fun showExcuseDialog() { val dialogBinding = DialogExcuseBinding.inflate(LayoutInflater.from(context)) - MaterialAlertDialogBuilder(requireContext()) + AlertDialog.Builder(requireContext()) .setTitle(R.string.attendance_excuse_title) .setView(dialogBinding.root) .setNegativeButton(android.R.string.cancel) { _, _ -> } @@ -285,9 +280,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - presenter.currentDate?.let { - outState.putLong(SAVED_DATE_KEY, it.toEpochDay()) - } + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) } override fun onDestroyView() { 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 f66479daf..26bfaf19f 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 @@ -3,14 +3,10 @@ package io.github.wulkanowy.ui.modules.attendance import android.annotation.SuppressLint import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.repositories.AttendanceRepository import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.* @@ -18,7 +14,6 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEach import timber.log.Timber -import java.time.DayOfWeek import java.time.LocalDate import java.time.LocalDate.now import java.time.LocalDate.ofEpochDay @@ -33,10 +28,9 @@ class AttendancePresenter @Inject constructor( private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { - private var initialDate: LocalDate? = null - private var isWeekendHasLessons: Boolean = false + private var baseDate: LocalDate = now().previousOrSameSchoolDay - var currentDate: LocalDate? = null + lateinit var currentDate: LocalDate private set private lateinit var lastError: Throwable @@ -50,34 +44,27 @@ class AttendancePresenter @Inject constructor( view.initView() Timber.i("Attendance view was initialized") errorHandler.showErrorMessage = ::showErrorViewOnError - currentDate = date?.let(::ofEpochDay) + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) loadData() + if (currentDate.isHolidays) setBaseDateOnHolidays() } fun onPreviousDay() { - val date = if (isWeekendHasLessons) { - currentDate?.minusDays(1) - } else currentDate?.previousSchoolDay - view?.finishActionMode() attendanceToExcuseList.clear() - reloadView(date ?: return) + reloadView(currentDate.previousSchoolDay) loadData() } fun onNextDay() { - val date = if (isWeekendHasLessons) { - currentDate?.plusDays(1) - } else currentDate?.nextSchoolDay - view?.finishActionMode() attendanceToExcuseList.clear() - reloadView(date ?: return) + reloadView(currentDate.nextSchoolDay) loadData() } fun onPickDate() { - view?.showDatePickerDialog(currentDate ?: return) + view?.showDatePickerDialog(currentDate) } fun onDateSet(year: Int, month: Int, day: Int) { @@ -106,8 +93,10 @@ class AttendancePresenter @Inject constructor( Timber.i("Attendance view is reselected") view?.let { view -> if (view.currentStackSize == 1) { - if (currentDate != initialDate) { - reloadView(initialDate ?: return) + baseDate = now().previousOrSameSchoolDay + + if (currentDate != baseDate) { + reloadView(baseDate) loadData() } else if (!view.isViewEmpty) { view.resetView() @@ -199,6 +188,19 @@ class AttendancePresenter @Inject constructor( return true } + private fun setBaseDateOnHolidays() { + flow { + val student = studentRepository.getCurrentStudent() + emit(semesterRepository.getCurrentSemester(student)) + }.catch { + Timber.i("Loading semester result: An exception occurred") + }.onEach { + baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear) + currentDate = baseDate + reloadNavigation() + }.launch("holidays") + } + private fun loadData(forceRefresh: Boolean = false) { Timber.i("Loading attendance data started") @@ -209,13 +211,11 @@ class AttendancePresenter @Inject constructor( isParent = student.isParent val semester = semesterRepository.getCurrentSemester(student) - - checkInitialAndCurrentDate(student, semester) attendanceRepository.getAttendance( student = student, semester = semester, - start = currentDate ?: now(), - end = currentDate ?: now(), + start = currentDate, + end = currentDate, forceRefresh = forceRefresh ) } @@ -231,8 +231,6 @@ class AttendancePresenter @Inject constructor( }.sortedBy { item -> item.number } } .onResourceData { - isWeekendHasLessons = isWeekendHasLessons || isWeekendHasLessons(it) - view?.run { enableSwipe(true) showProgress(false) @@ -240,7 +238,6 @@ class AttendancePresenter @Inject constructor( showEmpty(it.isEmpty()) showContent(it.isNotEmpty()) updateData(it) - reloadNavigation() } } .onResourceIntermediate { view?.showRefresh(true) } @@ -266,43 +263,6 @@ class AttendancePresenter @Inject constructor( .launch() } - private suspend fun checkInitialAndCurrentDate(student: Student, semester: Semester) { - if (initialDate == null) { - val lessons = attendanceRepository.getAttendance( - student = student, - semester = semester, - start = now().monday, - end = now().sunday, - forceRefresh = false, - ).toFirstResult().dataOrNull.orEmpty() - isWeekendHasLessons = isWeekendHasLessons(lessons) - initialDate = getInitialDate(semester) - } - - if (currentDate == null) { - currentDate = initialDate - } - } - - private fun isWeekendHasLessons( - lessons: List, - ): Boolean = lessons.any { - it.date.dayOfWeek in listOf( - DayOfWeek.SATURDAY, - DayOfWeek.SUNDAY, - ) - } - - private fun getInitialDate(semester: Semester): LocalDate { - val now = now() - - return when { - now.isHolidays -> now.getLastSchoolDayIfHoliday(semester.schoolYear) - isWeekendHasLessons -> now - else -> now.previousOrSameSchoolDay - } - } - private fun excuseAbsence(reason: String?, toExcuseList: List) { resourceFlow { val student = studentRepository.getCurrentStudent() @@ -351,7 +311,7 @@ class AttendancePresenter @Inject constructor( private fun reloadView(date: LocalDate) { currentDate = date - Timber.i("Reload attendance view with the date ${currentDate?.toFormattedString()}") + Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}") view?.apply { showProgress(true) enableSwipe(false) @@ -366,13 +326,10 @@ class AttendancePresenter @Inject constructor( @SuppressLint("DefaultLocale") private fun reloadNavigation() { - val currentDate = currentDate ?: return - view?.apply { showPreButton(!currentDate.minusDays(1).isHolidays) showNextButton(!currentDate.plusDays(1).isHolidays) updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise()) - showNavigation(true) } } } 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 2629c217e..b0123065a 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 @@ -40,8 +40,6 @@ interface AttendanceView : BaseView { fun showContent(show: Boolean) - fun showNavigation(show: Boolean) - fun showPreButton(show: Boolean) fun showNextButton(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthDialog.kt deleted file mode 100644 index fa29df473..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthDialog.kt +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.wulkanowy.ui.modules.auth - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.text.parseAsHtml -import androidx.core.view.isVisible -import androidx.core.widget.doOnTextChanged -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.databinding.DialogAuthBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import javax.inject.Inject - -@AndroidEntryPoint -class AuthDialog : BaseDialogFragment(), AuthView { - - @Inject - lateinit var presenter: AuthPresenter - - companion object { - fun newInstance() = AuthDialog() - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, R.style.FullScreenDialogStyle) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - return DialogAuthBinding.inflate(inflater).apply { binding = this }.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - presenter.onAttachView(this) - - binding.authInput.doOnTextChanged { text, _, _, _ -> - presenter.onPeselChange(text?.toString()) - } - - binding.authButton.setOnClickListener { presenter.authorize() } - binding.authSuccessButton.setOnClickListener { - activity?.recreate() - dismiss() - } - binding.authButtonSkip.setOnClickListener { dismiss() } - } - - override fun enableAuthButton(isEnabled: Boolean) { - binding.authButton.isEnabled = isEnabled - } - - override fun showProgress(show: Boolean) { - binding.authProgress.isVisible = show - } - - override fun showPeselError(show: Boolean) { - binding.authInputLayout.error = getString(R.string.auth_api_error).takeIf { show } - } - - override fun showInvalidPeselError(show: Boolean) { - binding.authInputLayout.error = getString(R.string.auth_invalid_error).takeIf { show } - } - - override fun showSuccess(show: Boolean) { - binding.authSuccess.isVisible = show - } - - override fun showContent(show: Boolean) { - binding.authForm.isVisible = show - } - - override fun showDescriptionWithName(name: String) { - binding.authDescription.text = getString(R.string.auth_description, name).parseAsHtml() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthPresenter.kt deleted file mode 100644 index 8f579712b..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthPresenter.kt +++ /dev/null @@ -1,100 +0,0 @@ -package io.github.wulkanowy.ui.modules.auth - -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import kotlinx.coroutines.launch -import javax.inject.Inject - -class AuthPresenter @Inject constructor( - private val semesterRepository: SemesterRepository, - errorHandler: ErrorHandler, - studentRepository: StudentRepository -) : BasePresenter(errorHandler, studentRepository) { - - private var pesel: String = "" - - override fun onAttachView(view: AuthView) { - super.onAttachView(view) - view.enableAuthButton(pesel.length == 11) - view.showSuccess(false) - view.showProgress(false) - - loadName() - } - - private fun loadName() { - presenterScope.launch { - runCatching { studentRepository.getCurrentStudent(false) } - .onSuccess { view?.showDescriptionWithName(it.studentName) } - .onFailure { errorHandler.dispatch(it) } - } - } - - fun onPeselChange(newPesel: String?) { - pesel = newPesel.orEmpty() - - view?.enableAuthButton(pesel.length == 11) - view?.showPeselError(false) - view?.showInvalidPeselError(false) - } - - fun authorize() { - presenterScope.launch { - view?.showProgress(true) - view?.showContent(false) - - if (!isValidPESEL(pesel)) { - view?.showInvalidPeselError(true) - view?.showProgress(false) - view?.showContent(true) - return@launch - } - - runCatching { - val student = studentRepository.getCurrentStudent() - val semester = semesterRepository.getCurrentSemester(student) - - val isSuccess = studentRepository.authorizePermission(student, semester, pesel) - if (isSuccess) { - studentRepository.refreshStudentName(student, semester) - } - isSuccess - } - .onFailure { errorHandler.dispatch(it) } - .onSuccess { - if (it) { - view?.showSuccess(true) - view?.showContent(false) - view?.showPeselError(false) - } else { - view?.showSuccess(false) - view?.showContent(true) - view?.showPeselError(true) - } - } - - view?.showProgress(false) - } - } - - private fun isValidPESEL(peselString: String): Boolean { - if (peselString.length != 11) { - return false - } - - val weights = intArrayOf(1, 3, 7, 9, 1, 3, 7, 9, 1, 3) - var sum = 0 - - for (i in 0 until 10) { - sum += weights[i] * Character.getNumericValue(peselString[i]) - } - - sum %= 10 - sum = 10 - sum - sum %= 10 - - return sum == Character.getNumericValue(peselString[10]) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthView.kt deleted file mode 100644 index d7e1917c2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthView.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.wulkanowy.ui.modules.auth - -import io.github.wulkanowy.ui.base.BaseView - -interface AuthView : BaseView { - - fun enableAuthButton(isEnabled: Boolean) - - fun showProgress(show: Boolean) - - fun showPeselError(show: Boolean) - - fun showInvalidPeselError(show: Boolean) - - fun showSuccess(show: Boolean) - - fun showContent(show: Boolean) - - fun showDescriptionWithName(name: String) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceDialog.kt index c532377e1..477b762b9 100644 --- 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 @@ -1,20 +1,19 @@ package io.github.wulkanowy.ui.modules.conference -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf +import android.view.ViewGroup import androidx.core.view.isVisible -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.data.db.entities.Conference import io.github.wulkanowy.databinding.DialogConferenceBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.serializable +import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.toFormattedString -@AndroidEntryPoint -class ConferenceDialog : BaseDialogFragment() { +class ConferenceDialog : DialogFragment() { + + private var binding: DialogConferenceBinding by lifecycleAwareVariable() private lateinit var conference: Conference @@ -23,20 +22,23 @@ class ConferenceDialog : BaseDialogFragment() { private const val ARGUMENT_KEY = "item" fun newInstance(conference: Conference) = ConferenceDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to conference) + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, conference) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - conference = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.let { + conference = it.getSerializable(ARGUMENT_KEY) as Conference + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogConferenceBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + 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) @@ -55,4 +57,4 @@ class ConferenceDialog : BaseDialogFragment() { 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 0cd3150c7..b9642b1c7 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 @@ -16,7 +16,7 @@ import javax.inject.Inject @AndroidEntryPoint class ConferenceFragment : BaseFragment(R.layout.fragment_conference), - ConferenceView, MainView.TitledView, MainView.MainChildView { + ConferenceView, MainView.TitledView { @Inject lateinit var presenter: ConferencePresenter @@ -109,14 +109,6 @@ class ConferenceFragment : BaseFragment(R.layout.frag (activity as? MainActivity)?.showDialogFragment(ConferenceDialog.newInstance(conference)) } - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onFragmentReselected() - } - - override fun resetView() { - binding.conferenceRecycler.smoothScrollToPosition(0) - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt index 1178c7200..f53648930 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 @@ -96,11 +96,4 @@ class ConferencePresenter @Inject constructor( .onResourceError(errorHandler::dispatch) .launch() } - - fun onFragmentReselected() { - Timber.i("Conference is reselected") - if (view?.isViewEmpty == false) { - view?.resetView() - } - } } 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 3299a1f08..4f73394df 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 @@ -28,6 +28,4 @@ interface ConferenceView : BaseView { fun showContent(show: Boolean) fun openConferenceDialog(conference: Conference) - - fun resetView() } 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 301262a04..de0b4a6c9 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 @@ -11,7 +11,6 @@ import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.databinding.FragmentDashboardBinding @@ -30,12 +29,7 @@ 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.dpToPx -import io.github.wulkanowy.utils.getErrorString -import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.openInternetBrowser -import io.github.wulkanowy.utils.toFormattedString +import io.github.wulkanowy.utils.* import java.time.LocalDate import javax.inject.Inject @@ -67,7 +61,6 @@ class DashboardFragment : BaseFragment(R.layout.fragme fun newInstance() = DashboardFragment() } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -154,7 +147,7 @@ class DashboardFragment : BaseFragment(R.layout.fragme val values = requireContext().resources.getStringArray(R.array.dashboard_tile_values) val selectedItemsState = values.map { value -> selectedItems.any { it.name == value } } - MaterialAlertDialogBuilder(requireContext()) + AlertDialog.Builder(requireContext()) .setTitle(R.string.pref_dashboard_appearance_tiles_title) .setMultiChoiceItems(entries, selectedItemsState.toBooleanArray()) { _, _, _ -> } .setPositiveButton(android.R.string.ok) { dialog, _ -> 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 d019dea68..e220ae236 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 @@ -33,27 +33,18 @@ sealed class DashboardItem(val type: Type) { } data class HorizontalGroup( - val unreadMessagesCount: Cell? = null, - val attendancePercentage: Cell? = null, - val luckyNumber: Cell? = null, + val unreadMessagesCount: Int? = null, + val attendancePercentage: Double? = null, + val luckyNumber: Int? = null, override val error: Throwable? = null, override val isLoading: Boolean = false ) : DashboardItem(Type.HORIZONTAL_GROUP) { - data class Cell( - val data: T?, - val error: Boolean, - val isLoading: Boolean, - ) { - val isHidden: Boolean - get() = data == null && !error && !isLoading - } - override val isDataLoaded - get() = unreadMessagesCount?.isLoading == false || attendancePercentage?.isLoading == false || luckyNumber?.isLoading == false + get() = unreadMessagesCount != null || attendancePercentage != null || luckyNumber != null val isFullDataLoaded - get() = luckyNumber?.isLoading != true && attendancePercentage?.isLoading != true && unreadMessagesCount?.isLoading != true + get() = luckyNumber != -1 && attendancePercentage != -1.0 && unreadMessagesCount != -1 } data class Grades( 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 f033b5947..9c15acc35 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 @@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.dashboard import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter -import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder import java.util.* class DashboardItemMoveCallback( @@ -56,5 +55,5 @@ class DashboardItemMoveCallback( } private val RecyclerView.ViewHolder.isAdminMessageOrAccountItem: Boolean - get() = this is AdminMessageViewHolder || this is DashboardAdapter.AccountViewHolder + 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 c93dd9e78..350300937 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt @@ -1,46 +1,17 @@ package io.github.wulkanowy.ui.modules.dashboard -import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.dataOrNull +import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.AdminMessage import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.enums.MessageFolder -import io.github.wulkanowy.data.enums.MessageType -import io.github.wulkanowy.data.errorOrNull -import io.github.wulkanowy.data.flatResourceFlow -import io.github.wulkanowy.data.mapResourceData -import io.github.wulkanowy.data.onResourceError -import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository -import io.github.wulkanowy.data.repositories.ConferenceRepository -import io.github.wulkanowy.data.repositories.ExamRepository -import io.github.wulkanowy.data.repositories.GradeRepository -import io.github.wulkanowy.data.repositories.HomeworkRepository -import io.github.wulkanowy.data.repositories.LuckyNumberRepository -import io.github.wulkanowy.data.repositories.MessageRepository -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.repositories.TimetableRepository -import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase +import io.github.wulkanowy.data.repositories.* import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AdsHelper import io.github.wulkanowy.utils.calculatePercentage import io.github.wulkanowy.utils.nextOrSameSchoolDay -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.emitAll -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNot -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber import java.time.Instant @@ -54,6 +25,7 @@ class DashboardPresenter @Inject constructor( private val gradeRepository: GradeRepository, private val semesterRepository: SemesterRepository, private val messageRepository: MessageRepository, + private val mailboxRepository: MailboxRepository, private val attendanceSummaryRepository: AttendanceSummaryRepository, private val timetableRepository: TimetableRepository, private val homeworkRepository: HomeworkRepository, @@ -61,7 +33,7 @@ class DashboardPresenter @Inject constructor( private val conferenceRepository: ConferenceRepository, private val preferencesRepository: PreferencesRepository, private val schoolAnnouncementRepository: SchoolAnnouncementRepository, - private val getAppropriateAdminMessageUseCase: GetAppropriateAdminMessageUseCase, + private val adminMessageRepository: AdminMessageRepository, private val adsHelper: AdsHelper ) : BasePresenter(errorHandler, studentRepository) { @@ -75,11 +47,6 @@ class DashboardPresenter @Inject constructor( private val firstLoadedItemList = mutableListOf() - private val selectedDashboardTiles - get() = preferencesRepository.selectedDashboardTiles - .filterNot { it == DashboardItem.Tile.ADS && !adsHelper.canShowAd } - .toSet() - private lateinit var lastError: Throwable override fun onAttachView(view: DashboardView) { @@ -91,19 +58,10 @@ class DashboardPresenter @Inject constructor( showContent(false) } - val selectedDashboardTilesFlow = preferencesRepository.selectedDashboardTilesFlow - .map { selectedDashboardTiles } - val isAdsEnabledFlow = preferencesRepository.isAdsEnabledFlow - .filter { (adsHelper.canShowAd && it) || !it } - .map { selectedDashboardTiles } - val isMobileAdsSdkInitializedFlow = adsHelper.isMobileAdsSdkInitialized - .filter { it } - .map { selectedDashboardTiles } - merge( - selectedDashboardTilesFlow, - isAdsEnabledFlow, - isMobileAdsSdkInitializedFlow + preferencesRepository.selectedDashboardTilesFlow, + preferencesRepository.isAdsEnabledFlow + .map { preferencesRepository.selectedDashboardTiles } ) .onEach { loadData(tilesToLoad = it) } .launch("dashboard_pref") @@ -112,7 +70,7 @@ class DashboardPresenter @Inject constructor( fun onAdminMessageDismissed(adminMessage: AdminMessage) { preferencesRepository.dismissedAdminMessageIds += adminMessage.id - loadData(selectedDashboardTiles) + loadData(preferencesRepository.selectedDashboardTiles) } fun onDragAndDropEnd(list: List) { @@ -202,23 +160,19 @@ class DashboardPresenter @Inject constructor( DashboardItem.Type.ACCOUNT -> { updateData(DashboardItem.Account(student), forceRefresh) } - DashboardItem.Type.HORIZONTAL_GROUP -> { loadHorizontalGroup(student, forceRefresh) } - DashboardItem.Type.LESSONS -> loadLessons(student, forceRefresh) DashboardItem.Type.GRADES -> loadGrades(student, forceRefresh) DashboardItem.Type.HOMEWORK -> loadHomework(student, forceRefresh) DashboardItem.Type.ANNOUNCEMENTS -> { loadSchoolAnnouncements(student, forceRefresh) } - DashboardItem.Type.EXAMS -> loadExams(student, forceRefresh) DashboardItem.Type.CONFERENCES -> { loadConferences(student, forceRefresh) } - DashboardItem.Type.ADS -> loadAds(forceRefresh) DashboardItem.Type.ADMIN_MESSAGE -> loadAdminMessage(student, forceRefresh) } @@ -228,7 +182,7 @@ class DashboardPresenter @Inject constructor( fun onSwipeRefresh() { Timber.i("Force refreshing the dashboard") - loadData(selectedDashboardTiles, forceRefresh = true) + loadData(preferencesRepository.selectedDashboardTiles, forceRefresh = true) } fun onRetry() { @@ -236,7 +190,7 @@ class DashboardPresenter @Inject constructor( showErrorView(false) showProgress(true) } - loadData(selectedDashboardTiles, forceRefresh = true) + loadData(preferencesRepository.selectedDashboardTiles, forceRefresh = true) } fun onViewReselected() { @@ -257,7 +211,7 @@ class DashboardPresenter @Inject constructor( } fun onDashboardTileSettingsSelected(): Boolean { - view?.showDashboardTileSettings(selectedDashboardTiles.toList()) + view?.showDashboardTileSettings(preferencesRepository.selectedDashboardTiles.toList()) return true } @@ -273,71 +227,50 @@ class DashboardPresenter @Inject constructor( private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) { flow { - val selectedTiles = selectedDashboardTiles - val flowSuccess = flowOf(Resource.Success(null)) + val semester = semesterRepository.getCurrentSemester(student) + val mailbox = mailboxRepository.getMailbox(student) + val selectedTiles = preferencesRepository.selectedDashboardTiles + val flowSuccess = flowOf(Resource.Success(null)) val luckyNumberFlow = luckyNumberRepository.getLuckyNumber(student, forceRefresh) .mapResourceData { it ?: LuckyNumber(0, LocalDate.now(), 0) } - .onResourceError { errorHandler.dispatch(it) } .takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowSuccess - val messageFLow = flatResourceFlow { - val mailbox = messageRepository.getMailboxByStudent(student) + val messageFLow = messageRepository.getMessages( + student = student, + mailbox = mailbox, + folder = MessageFolder.RECEIVED, + forceRefresh = forceRefresh + ).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess - messageRepository.getMessages( - student = student, - mailbox = mailbox, - folder = MessageFolder.RECEIVED, - forceRefresh = forceRefresh - ) - } - .onResourceError { errorHandler.dispatch(it) } - .takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess - - val attendanceFlow = flatResourceFlow { - val semester = semesterRepository.getCurrentSemester(student) - attendanceSummaryRepository.getAttendanceSummary( - student = student, - semester = semester, - subjectId = -1, - forceRefresh = forceRefresh - ) - } - .onResourceError { errorHandler.dispatch(it) } - .takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess + val attendanceFlow = attendanceSummaryRepository.getAttendanceSummary( + student = student, + semester = semester, + subjectId = -1, + forceRefresh = forceRefresh + ).takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess emitAll( combine( - flow = luckyNumberFlow, - flow2 = messageFLow, - flow3 = attendanceFlow, + luckyNumberFlow, + messageFLow, + attendanceFlow ) { luckyNumberResource, messageResource, attendanceResource -> val resList = listOf(luckyNumberResource, messageResource, attendanceResource) + resList.firstNotNullOfOrNull { it.errorOrNull }?.let { throw it } + val isLoading = resList.any { it is Resource.Loading } + + val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber + val messageCount = messageResource.dataOrNull?.count { it.unread } + val attendancePercentage = attendanceResource.dataOrNull?.calculatePercentage() DashboardItem.HorizontalGroup( - isLoading = resList.any { it is Resource.Loading }, - error = resList.map { it.errorOrNull }.let { errors -> - if (errors.all { it != null }) { - errors.firstOrNull() - } else null - }, - attendancePercentage = DashboardItem.HorizontalGroup.Cell( - data = attendanceResource.dataOrNull?.calculatePercentage(), - error = attendanceResource.errorOrNull != null, - isLoading = attendanceResource is Resource.Loading, - ), - unreadMessagesCount = DashboardItem.HorizontalGroup.Cell( - data = messageResource.dataOrNull?.count { it.unread }, - error = messageResource.errorOrNull != null, - isLoading = messageResource is Resource.Loading, - ), - luckyNumber = DashboardItem.HorizontalGroup.Cell( - data = luckyNumberResource.dataOrNull?.luckyNumber, - error = luckyNumberResource.errorOrNull != null, - isLoading = luckyNumberResource is Resource.Loading, - ) + isLoading = isLoading, + attendancePercentage = if (attendancePercentage == 0.0 && isLoading) -1.0 else attendancePercentage, + unreadMessagesCount = if (messageCount == 0 && isLoading) -1 else messageCount, + luckyNumber = if (luckyNumber == 0 && isLoading) -1 else luckyNumber ) }) } @@ -348,8 +281,11 @@ class DashboardPresenter @Inject constructor( if (it.isLoading) { Timber.i("Loading horizontal group data started") + + if (it.isFullDataLoaded) { + firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP + } } else { - firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP Timber.i("Loading horizontal group result: Success") } } @@ -402,7 +338,6 @@ class DashboardPresenter @Inject constructor( firstLoadedItemList += DashboardItem.Type.GRADES } } - is Resource.Success -> { Timber.i("Loading dashboard grades result: Success") updateData( @@ -413,7 +348,6 @@ class DashboardPresenter @Inject constructor( forceRefresh ) } - is Resource.Error -> { Timber.i("Loading dashboard grades result: An exception occurred") errorHandler.dispatch(it.error) @@ -427,7 +361,7 @@ class DashboardPresenter @Inject constructor( private fun loadLessons(student: Student, forceRefresh: Boolean) { flatResourceFlow { val semester = semesterRepository.getCurrentSemester(student) - val date = LocalDate.now() + val date = LocalDate.now().nextOrSameSchoolDay timetableRepository.getTimetable( student = student, @@ -451,14 +385,12 @@ class DashboardPresenter @Inject constructor( firstLoadedItemList += DashboardItem.Type.LESSONS } } - is Resource.Success -> { Timber.i("Loading dashboard lessons result: Success") updateData( DashboardItem.Lessons(it.data), forceRefresh ) } - is Resource.Error -> { Timber.i("Loading dashboard lessons result: An exception occurred") errorHandler.dispatch(it.error) @@ -508,12 +440,10 @@ class DashboardPresenter @Inject constructor( firstLoadedItemList += DashboardItem.Type.HOMEWORK } } - is Resource.Success -> { Timber.i("Loading dashboard homework result: Success") updateData(DashboardItem.Homework(it.data), forceRefresh) } - is Resource.Error -> { Timber.i("Loading dashboard homework result: An exception occurred") errorHandler.dispatch(it.error) @@ -542,12 +472,10 @@ class DashboardPresenter @Inject constructor( firstLoadedItemList += DashboardItem.Type.ANNOUNCEMENTS } } - is Resource.Success -> { Timber.i("Loading dashboard announcements result: Success") updateData(DashboardItem.Announcements(it.data), forceRefresh) } - is Resource.Error -> { Timber.i("Loading dashboard announcements result: An exception occurred") errorHandler.dispatch(it.error) @@ -585,12 +513,10 @@ class DashboardPresenter @Inject constructor( firstLoadedItemList += DashboardItem.Type.EXAMS } } - is Resource.Success -> { Timber.i("Loading dashboard exams result: Success") updateData(DashboardItem.Exams(it.data), forceRefresh) } - is Resource.Error -> { Timber.i("Loading dashboard exams result: An exception occurred") errorHandler.dispatch(it.error) @@ -626,12 +552,10 @@ class DashboardPresenter @Inject constructor( firstLoadedItemList += DashboardItem.Type.CONFERENCES } } - is Resource.Success -> { Timber.i("Loading dashboard conferences result: Success") updateData(DashboardItem.Conferences(it.data), forceRefresh) } - is Resource.Error -> { Timber.i("Loading dashboard conferences result: An exception occurred") errorHandler.dispatch(it.error) @@ -643,12 +567,12 @@ class DashboardPresenter @Inject constructor( } private fun loadAdminMessage(student: Student, forceRefresh: Boolean) { - flatResourceFlow { - getAppropriateAdminMessageUseCase( - student = student, - type = MessageType.DASHBOARD_MESSAGE, - ) - } + flatResourceFlow { adminMessageRepository.getAdminMessages(student) } + .filter { + val data = it.dataOrNull ?: return@filter true + val isDismissed = data.id in preferencesRepository.dismissedAdminMessageIds + !isDismissed + } .onEach { when (it) { is Resource.Loading -> { @@ -656,7 +580,6 @@ class DashboardPresenter @Inject constructor( if (forceRefresh) return@onEach updateData(DashboardItem.AdminMessages(), forceRefresh) } - is Resource.Success -> { Timber.i("Loading dashboard admin message result: Success") updateData( @@ -664,10 +587,9 @@ class DashboardPresenter @Inject constructor( forceRefresh = forceRefresh ) } - is Resource.Error -> { Timber.i("Loading dashboard admin message result: An exception occurred") - Timber.e(it.error) + errorHandler.dispatch(it.error) updateData( dashboardItem = DashboardItem.AdminMessages( adminMessage = null, @@ -809,7 +731,7 @@ class DashboardPresenter @Inject constructor( itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null val isGeneralError = filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError - val firstError = itemsLoadedList.firstNotNullOfOrNull { it.error } + val firstError = itemsLoadedList.mapNotNull { it.error }.firstOrNull() val filteredOriginalLoadedList = dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt index 7c74cae80..a3c423a8b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt @@ -1,6 +1,8 @@ package io.github.wulkanowy.ui.modules.dashboard.adapters import android.annotation.SuppressLint +import android.content.res.ColorStateList +import android.graphics.Color import android.graphics.Typeface import android.os.Handler import android.os.Looper @@ -22,7 +24,6 @@ import io.github.wulkanowy.data.db.entities.TimetableHeader import io.github.wulkanowy.data.enums.GradeColorTheme import io.github.wulkanowy.databinding.* import io.github.wulkanowy.ui.modules.dashboard.DashboardItem -import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder import io.github.wulkanowy.utils.* import timber.log.Timber import java.time.Duration @@ -108,9 +109,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter AdminMessageViewHolder( - ItemDashboardAdminMessageBinding.inflate(inflater, parent, false), - onAdminMessageDismissClickListener = onAdminMessageDismissClickListener, - onAdminMessageClickListener = onAdminMessageClickListener, + ItemDashboardAdminMessageBinding.inflate(inflater, parent, false) ) DashboardItem.Type.ADS.ordinal -> AdsViewHolder( ItemDashboardAdsBinding.inflate(inflater, parent, false) @@ -129,7 +128,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter bindAnnouncementsViewHolder(holder, position) is ExamsViewHolder -> bindExamsViewHolder(holder, position) is ConferencesViewHolder -> bindConferencesViewHolder(holder, position) - is AdminMessageViewHolder -> holder.bind((items[position] as DashboardItem.AdminMessages).adminMessage) + is AdminMessageViewHolder -> bindAdminMessage(holder, position) is AdsViewHolder -> bindAdsViewHolder(holder, position) } } @@ -172,105 +171,81 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter { - updateMarginsRelative( - end = if (isAttendanceHidden && isMessagesHidden && !isLuckyNumberHidden) { - 0 - } else context.dpToPx(8f).toInt() - ) - } - } - } - - private fun ItemDashboardHorizontalGroupBinding.bindMessages( - item: DashboardItem.HorizontalGroup, - isWideErrorShow: Boolean - ) { - dashboardHorizontalGroupItemMessageError.isVisible = item.unreadMessagesCount?.error == true - with(dashboardHorizontalGroupItemMessageValue) { - isVisible = item.unreadMessagesCount?.error != true - text = item.unreadMessagesCount?.data.toString() - } - with(dashboardHorizontalGroupItemMessageContainer) { - isVisible = item.unreadMessagesCount?.isHidden == false && !isWideErrorShow - setOnClickListener { onMessageTileClickListener() } - } - } - - private fun ItemDashboardHorizontalGroupBinding.bindAttendance( - item: DashboardItem.HorizontalGroup, - isWideErrorShow: Boolean - ) { - val attendancePercentage = item.attendancePercentage?.data + (isLoading && !item.isDataLoaded) || (isLoading && !item.isFullDataLoaded) val attendanceColor = when { attendancePercentage == null || attendancePercentage == .0 -> { - root.context.getThemeAttrColor(R.attr.colorOnSurface) + context.getThemeAttrColor(R.attr.colorOnSurface) } attendancePercentage <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> { - root.context.getThemeAttrColor(R.attr.colorPrimary) + context.getThemeAttrColor(R.attr.colorPrimary) } attendancePercentage <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> { - root.context.getThemeAttrColor(R.attr.colorTimetableChange) + context.getThemeAttrColor(R.attr.colorTimetableChange) } - else -> root.context.getThemeAttrColor(R.attr.colorOnSurface) + else -> context.getThemeAttrColor(R.attr.colorOnSurface) } val attendanceString = if (attendancePercentage == null || attendancePercentage == .0) { - root.context.getString(R.string.dashboard_horizontal_group_no_data) + context.getString(R.string.dashboard_horizontal_group_no_data) } else { "%.2f%%".format(attendancePercentage) } - dashboardHorizontalGroupItemAttendanceError.isVisible = - item.attendancePercentage?.error == true - with(dashboardHorizontalGroupItemAttendanceValue) { - isVisible = item.attendancePercentage?.error != true + with(binding.dashboardHorizontalGroupItemAttendanceValue) { text = attendanceString setTextColor(attendanceColor) } - with(dashboardHorizontalGroupItemAttendanceContainer) { - isVisible = item.attendancePercentage?.isHidden == false && !isWideErrorShow - setOnClickListener { onAttendanceTileClickListener() } - updateLayoutParams { - matchConstraintPercentWidth = when { - item.luckyNumber?.isHidden == true && item.unreadMessagesCount?.isHidden == true -> 1.0f - item.luckyNumber?.isHidden == true || item.unreadMessagesCount?.isHidden == true -> 0.5f - else -> 0.4f + + with(binding) { + dashboardHorizontalGroupItemMessageValue.text = unreadMessagesCount.toString() + dashboardHorizontalGroupItemLuckyValue.text = if (luckyNumber == 0) { + context.getString(R.string.dashboard_horizontal_group_no_data) + } else luckyNumber?.toString() + + dashboardHorizontalGroupItemInfoContainer.isVisible = error != null || isLoadingVisible + dashboardHorizontalGroupItemInfoProgress.isVisible = isLoadingVisible + dashboardHorizontalGroupItemInfoErrorText.isVisible = error != null + + with(dashboardHorizontalGroupItemLuckyContainer) { + isVisible = luckyNumber != null && luckyNumber != -1 && !isLoadingVisible + setOnClickListener { onLuckyNumberTileClickListener() } + + updateLayoutParams { + updateMarginsRelative( + end = if (attendancePercentage == null && unreadMessagesCount == null && luckyNumber != null) { + 0 + } else { + context.dpToPx(8f).toInt() + } + ) } } + + with(dashboardHorizontalGroupItemAttendanceContainer) { + isVisible = + attendancePercentage != null && attendancePercentage != -1.0 && !isLoadingVisible + updateLayoutParams { + matchConstraintPercentWidth = when { + luckyNumber == null && unreadMessagesCount == null -> 1.0f + luckyNumber == null || unreadMessagesCount == null -> 0.5f + else -> 0.4f + } + } + setOnClickListener { onAttendanceTileClickListener() } + } + + with(dashboardHorizontalGroupItemMessageContainer) { + isVisible = + unreadMessagesCount != null && unreadMessagesCount != -1 && !isLoadingVisible + setOnClickListener { onMessageTileClickListener() } + } } } @@ -734,6 +709,38 @@ 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) } + } + } + } + private fun bindAdsViewHolder(adsViewHolder: AdsViewHolder, position: Int) { val item = (items[position] as DashboardItem.Ads).adBanner ?: return val binding = adsViewHolder.binding @@ -787,6 +794,9 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter() { @@ -39,9 +37,7 @@ class DashboardGradesAdapter : RecyclerView.Adapter Unit, - private val onAdminMessageClickListener: (String?) -> Unit, -) : RecyclerView.ViewHolder(binding.root) { - - fun bind(item: AdminMessage?) { - item ?: return - - val context = binding.root.context - val (backgroundColor, textColor) = when (item.priority) { - "HIGH" -> { - context.getThemeAttrColor(R.attr.colorMessageHigh) to - context.getThemeAttrColor(R.attr.colorOnMessageHigh) - } - "MEDIUM" -> { - context.getThemeAttrColor(R.attr.colorMessageMedium) to Color.BLACK - } - else -> null to context.getThemeAttrColor(R.attr.colorOnSurface) - } - - with(binding) { - dashboardAdminMessageItemTitle.text = item.title - dashboardAdminMessageItemTitle.setTextColor(textColor) - dashboardAdminMessageItemDescription.text = item.content - dashboardAdminMessageItemDescription.setTextColor(textColor) - dashboardAdminMessageItemIcon.setColorFilter(textColor) - dashboardAdminMessageItemDismiss.isVisible = item.isDismissible - dashboardAdminMessageItemDismiss.setTextColor(textColor) - dashboardAdminMessageItemDismiss.setOnClickListener { - onAdminMessageDismissClickListener(item) - } - - root.setCardBackgroundColor(backgroundColor?.let { ColorStateList.valueOf(it) }) - item.destinationUrl?.let { url -> - root.setOnClickListener { onAdminMessageClickListener(url) } - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt index 929e72645..1e11c874b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt @@ -1,7 +1,9 @@ package io.github.wulkanowy.ui.modules.debug.logviewer import android.content.Intent -import android.content.Intent.* +import android.content.Intent.EXTRA_EMAIL +import android.content.Intent.EXTRA_STREAM +import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION import android.os.Bundle import android.view.Menu import android.view.MenuInflater @@ -34,7 +36,6 @@ class LogViewerFragment : BaseFragment(R.layout.fragme fun newInstance() = LogViewerFragment() } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt index 27d8613a3..6ff26162b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt @@ -19,12 +19,9 @@ val debugMessageItems = listOf( private fun generateMessage(sender: String, subject: String) = Message( subject = subject, messageId = 123, - email = "", date = Instant.now(), folderId = 0, unread = true, - readBy = 2, - unreadBy = 2, hasAttachments = false, messageGlobalKey = "", correspondents = sender, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt index d452d74a5..41adc008a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt @@ -1,22 +1,21 @@ package io.github.wulkanowy.ui.modules.exam -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.databinding.DialogExamBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment +import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.openCalendarEventAdd -import io.github.wulkanowy.utils.serializable import io.github.wulkanowy.utils.toFormattedString import java.time.LocalTime -@AndroidEntryPoint -class ExamDialog : BaseDialogFragment() { +class ExamDialog : DialogFragment() { + + private var binding: DialogExamBinding by lifecycleAwareVariable() private lateinit var exam: Exam @@ -25,20 +24,23 @@ class ExamDialog : BaseDialogFragment() { private const val ARGUMENT_KEY = "Item" fun newInstance(exam: Exam) = ExamDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to exam) + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - exam = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + exam = getSerializable(ARGUMENT_KEY) as Exam + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogExamBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogExamBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) 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 0123e2340..ddd0e4a19 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt @@ -2,7 +2,9 @@ package io.github.wulkanowy.ui.modules.exam import android.os.Bundle import android.view.View -import android.view.View.* +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -18,7 +20,7 @@ import javax.inject.Inject @AndroidEntryPoint class ExamFragment : BaseFragment(R.layout.fragment_exam), ExamView, - MainView.TitledView, MainView.MainChildView { + MainView.TitledView { @Inject lateinit var presenter: ExamPresenter @@ -62,7 +64,7 @@ class ExamFragment : BaseFragment(R.layout.fragment_exam), examPreviousButton.setOnClickListener { presenter.onPreviousWeek() } examNextButton.setOnClickListener { presenter.onNextWeek() } - examNavContainer.elevation = requireContext().dpToPx(3f) + examNavContainer.elevation = requireContext().dpToPx(8f) } } @@ -124,14 +126,6 @@ class ExamFragment : BaseFragment(R.layout.fragment_exam), (activity as? MainActivity)?.showDialogFragment(ExamDialog.newInstance(exam)) } - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onViewReselected() - } - - override fun resetView() { - binding.examRecycler.smoothScrollToPosition(0) - } - override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt index 858140728..99b0bcb87 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt @@ -175,17 +175,4 @@ class ExamPresenter @Inject constructor( ) } } - - fun onViewReselected() { - Timber.i("Exam view is reselected") - - baseDate = now().nextOrSameSchoolDay - - if (currentDate != baseDate) { - reloadView(baseDate) - loadData() - } else if (view?.isViewEmpty == false) { - view?.resetView() - } - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt index 677fac40e..45b9e788c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt @@ -34,6 +34,4 @@ interface ExamView : BaseView { fun showPreButton(show: Boolean) fun showExamDialog(exam: Exam) - - fun resetView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt index ec4bd8e8c..b6733d4f2 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 @@ -12,92 +12,70 @@ import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.* import io.github.wulkanowy.utils.calcAverage import io.github.wulkanowy.utils.changeModifier -import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest import javax.inject.Inject -@OptIn(ExperimentalCoroutinesApi::class) +@OptIn(FlowPreview::class) class GradeAverageProvider @Inject constructor( private val semesterRepository: SemesterRepository, private val gradeRepository: GradeRepository, private val preferencesRepository: PreferencesRepository ) { - private data class AverageCalcParams( - val gradeAverageMode: GradeAverageMode, - val forceAverageCalc: Boolean, - val isOptionalArithmeticAverage: Boolean, - val plusModifier: Double, - val minusModifier: Double, - ) + private val plusModifier get() = preferencesRepository.gradePlusModifier - fun getGradesDetailsWithAverage( - student: Student, - semesterId: Int, - forceRefresh: Boolean - ): Flow>> = combine( - flow = preferencesRepository.gradeAverageModeFlow, - flow2 = preferencesRepository.gradeAverageForceCalcFlow, - flow3 = preferencesRepository.isOptionalArithmeticAverageFlow, - flow4 = preferencesRepository.gradePlusModifierFlow, - flow5 = preferencesRepository.gradeMinusModifierFlow, - ) { gradeAverageMode, forceAverageCalc, isOptionalArithmeticAverage, plusModifier, minusModifier -> - AverageCalcParams( - gradeAverageMode = gradeAverageMode, - forceAverageCalc = forceAverageCalc, - isOptionalArithmeticAverage = isOptionalArithmeticAverage, - plusModifier = plusModifier, - minusModifier = minusModifier, - ) - }.flatMapLatest { params -> + private val minusModifier get() = preferencesRepository.gradeMinusModifier + + private val isOptionalArithmeticAverage get() = preferencesRepository.isOptionalArithmeticAverage + + fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) = flatResourceFlow { val semesters = semesterRepository.getSemesters(student) - when (params.gradeAverageMode) { + + when (preferencesRepository.gradeAverageMode) { ONE_SEMESTER -> getGradeSubjects( student = student, - semester = semesters.first { it.semesterId == semesterId }, - forceRefresh = forceRefresh, - params = params, + semester = semesters.single { it.semesterId == semesterId }, + forceRefresh = forceRefresh ) BOTH_SEMESTERS -> calculateCombinedAverage( student = student, semesters = semesters, semesterId = semesterId, forceRefresh = forceRefresh, - config = params, + averageMode = BOTH_SEMESTERS ) ALL_YEAR -> calculateCombinedAverage( student = student, semesters = semesters, semesterId = semesterId, forceRefresh = forceRefresh, - config = params, + averageMode = ALL_YEAR ) } - } - }.distinctUntilChanged() + }.distinctUntilChanged() private fun calculateCombinedAverage( student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean, - config: AverageCalcParams, + averageMode: GradeAverageMode ): Flow>> { + val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc val selectedSemester = semesters.single { it.semesterId == semesterId } val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 } val selectedSemesterGradeSubjects = - getGradeSubjects(student, selectedSemester, forceRefresh, config) + getGradeSubjects(student, selectedSemester, forceRefresh) if (selectedSemester == firstSemester) return selectedSemesterGradeSubjects - val firstSemesterGradeSubjects = - getGradeSubjects(student, firstSemester, forceRefresh, config) + val firstSemesterGradeSubjects = getGradeSubjects(student, firstSemester, forceRefresh) return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject -> if (firstSemesterGradeSubject.errorOrNull != null) { @@ -113,21 +91,21 @@ class GradeAverageProvider @Inject constructor( val firstSemesterSubject = firstSemesterGradeSubject.dataOrNull.orEmpty() .singleOrNull { it.subject == secondSemesterSubject.subject } - val updatedAverage = if (config.gradeAverageMode == ALL_YEAR) { + val updatedAverage = if (averageMode == ALL_YEAR) { calculateAllYearAverage( student = student, isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester, + isGradeAverageForceCalc = isGradeAverageForceCalc, secondSemesterSubject = secondSemesterSubject, - firstSemesterSubject = firstSemesterSubject, - config = config, + firstSemesterSubject = firstSemesterSubject ) } else { calculateBothSemestersAverage( student = student, isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester, + isGradeAverageForceCalc = isGradeAverageForceCalc, secondSemesterSubject = secondSemesterSubject, - firstSemesterSubject = firstSemesterSubject, - config = config + firstSemesterSubject = firstSemesterSubject ) } secondSemesterSubject.copy(average = updatedAverage) @@ -139,17 +117,17 @@ class GradeAverageProvider @Inject constructor( private fun calculateAllYearAverage( student: Student, isAnyVulcanAverage: Boolean, + isGradeAverageForceCalc: Boolean, secondSemesterSubject: GradeSubject, - firstSemesterSubject: GradeSubject?, - config: AverageCalcParams, - ) = if (!isAnyVulcanAverage || config.forceAverageCalc) { - val updatedSecondSemesterGrades = secondSemesterSubject.grades - .updateModifiers(student, config) - val updatedFirstSemesterGrades = firstSemesterSubject?.grades - ?.updateModifiers(student, config).orEmpty() + firstSemesterSubject: GradeSubject? + ) = if (!isAnyVulcanAverage || isGradeAverageForceCalc) { + val updatedSecondSemesterGrades = + secondSemesterSubject.grades.updateModifiers(student) + val updatedFirstSemesterGrades = + firstSemesterSubject?.grades?.updateModifiers(student).orEmpty() (updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage( - config.isOptionalArithmeticAverage + isOptionalArithmeticAverage ) } else { secondSemesterSubject.average @@ -158,35 +136,32 @@ class GradeAverageProvider @Inject constructor( private fun calculateBothSemestersAverage( student: Student, isAnyVulcanAverage: Boolean, + isGradeAverageForceCalc: Boolean, secondSemesterSubject: GradeSubject, - firstSemesterSubject: GradeSubject?, - config: AverageCalcParams, - ): Double { - return if (!isAnyVulcanAverage || config.forceAverageCalc) { - val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1 - val secondSemesterAverage = secondSemesterSubject.grades - .updateModifiers(student, config) - .calcAverage(config.isOptionalArithmeticAverage) - val firstSemesterAverage = firstSemesterSubject?.grades - ?.updateModifiers(student, config) - ?.calcAverage(config.isOptionalArithmeticAverage) ?: secondSemesterAverage + firstSemesterSubject: GradeSubject? + ): Double = if (!isAnyVulcanAverage || isGradeAverageForceCalc) { + val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1 - (secondSemesterAverage + firstSemesterAverage) / divider - } else { - val divider = if (secondSemesterSubject.average > 0) 2 else 1 + val secondSemesterAverage = secondSemesterSubject.grades.updateModifiers(student) + .calcAverage(isOptionalArithmeticAverage) + val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student) + ?.calcAverage(isOptionalArithmeticAverage) ?: secondSemesterAverage - secondSemesterSubject.average.plus( - (firstSemesterSubject?.average ?: secondSemesterSubject.average) - ) / divider - } + (secondSemesterAverage + firstSemesterAverage) / divider + } else { + val divider = if (secondSemesterSubject.average > 0) 2 else 1 + + (secondSemesterSubject.average + (firstSemesterSubject?.average + ?: secondSemesterSubject.average)) / divider } private fun getGradeSubjects( student: Student, semester: Semester, - forceRefresh: Boolean, - params: AverageCalcParams, + forceRefresh: Boolean ): Flow>> { + val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc + return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh) .mapResourceData { res -> val (details, summaries) = res @@ -197,15 +172,13 @@ class GradeAverageProvider @Inject constructor( student = student, semester = semester, grades = allGrades.toList(), - calcAverage = isAnyAverage, - params = params, + calcAverage = isAnyAverage ).map { summary -> val grades = allGrades[summary.subject].orEmpty() GradeSubject( subject = summary.subject, - average = if (!isAnyAverage || params.forceAverageCalc) { - grades.updateModifiers(student, params) - .calcAverage(params.isOptionalArithmeticAverage) + average = if (!isAnyAverage || isGradeAverageForceCalc) { + grades.updateModifiers(student).calcAverage(isOptionalArithmeticAverage) } else summary.average, points = summary.pointsSum, summary = summary, @@ -222,8 +195,7 @@ class GradeAverageProvider @Inject constructor( student: Student, semester: Semester, grades: List>>, - calcAverage: Boolean, - params: AverageCalcParams, + calcAverage: Boolean ): List { if (isNotEmpty() && size > grades.size) return this @@ -239,16 +211,15 @@ class GradeAverageProvider @Inject constructor( proposedPoints = "", finalPoints = "", pointsSum = "", - average = if (calcAverage) details.updateModifiers(student, params) - .calcAverage(params.isOptionalArithmeticAverage) else .0 + average = if (calcAverage) details.updateModifiers(student) + .calcAverage(isOptionalArithmeticAverage) else .0 ) } } - private fun List.updateModifiers( - student: Student, - params: AverageCalcParams, - ): List = if (student.loginMode == Sdk.Mode.SCRAPPER.name) { - map { it.changeModifier(params.plusModifier, params.minusModifier) } - } else this + private fun List.updateModifiers(student: Student): List { + return if (student.loginMode == Sdk.Mode.SCRAPPER.name) { + map { it.changeModifier(plusModifier, minusModifier) } + } else this + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt index 7ce07eb68..0a8561eec 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.dialog.MaterialAlertDialogBuilder import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -52,7 +51,6 @@ class GradeFragment : BaseFragment(R.layout.fragment_grade override val currentPageIndex get() = binding.gradeViewPager.currentItem - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -142,7 +140,7 @@ class GradeFragment : BaseFragment(R.layout.fragment_grade val choices = semesters.map { getString(R.string.grade_semester, it.semesterName) } .toTypedArray() - MaterialAlertDialogBuilder(requireContext()) + AlertDialog.Builder(requireContext()) .setSingleChoiceItems(choices, selectedIndex) { dialog, which -> presenter.onSemesterSelected(which) dialog.dismiss() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt index 15b5db031..e5c3bb63e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsAdapter.kt @@ -1,7 +1,6 @@ package io.github.wulkanowy.ui.modules.grade.details import android.annotation.SuppressLint -import android.content.res.ColorStateList import android.content.res.Resources import android.view.LayoutInflater import android.view.View @@ -18,10 +17,9 @@ import io.github.wulkanowy.databinding.HeaderGradeDetailsBinding import io.github.wulkanowy.databinding.ItemGradeDetailsBinding import io.github.wulkanowy.ui.base.BaseExpandableAdapter import io.github.wulkanowy.utils.getBackgroundColor -import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.toFormattedString import timber.log.Timber -import java.util.* +import java.util.BitSet import javax.inject.Inject class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter() { @@ -205,9 +203,7 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter grade.description diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt index 39f72f8bc..34594111f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt @@ -1,23 +1,21 @@ package io.github.wulkanowy.ui.modules.grade.details -import android.app.Dialog -import android.content.res.ColorStateList import android.os.Bundle +import android.view.LayoutInflater import android.view.View import android.view.View.GONE -import androidx.core.content.ContextCompat -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.enums.GradeColorTheme import io.github.wulkanowy.databinding.DialogGradeBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment import io.github.wulkanowy.utils.* -@AndroidEntryPoint -class GradeDetailsDialog : BaseDialogFragment() { + +class GradeDetailsDialog : DialogFragment() { + + private var binding: DialogGradeBinding by lifecycleAwareVariable() private lateinit var grade: Grade @@ -29,25 +27,29 @@ class GradeDetailsDialog : BaseDialogFragment() { private const val COLOR_THEME_KEY = "Theme" - fun newInstance(grade: Grade, colorTheme: GradeColorTheme) = GradeDetailsDialog().apply { - arguments = bundleOf( - ARGUMENT_KEY to grade, - COLOR_THEME_KEY to colorTheme - ) - } + fun newInstance(grade: Grade, colorTheme: GradeColorTheme) = + GradeDetailsDialog().apply { + arguments = Bundle().apply { + putSerializable(ARGUMENT_KEY, grade) + putSerializable(COLOR_THEME_KEY, colorTheme) + } + } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - grade = requireArguments().serializable(ARGUMENT_KEY) - gradeColorTheme = requireArguments().serializable(COLOR_THEME_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + grade = getSerializable(ARGUMENT_KEY) as Grade + gradeColorTheme = getSerializable(COLOR_THEME_KEY) as GradeColorTheme + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogGradeBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogGradeBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -55,9 +57,10 @@ class GradeDetailsDialog : BaseDialogFragment() { with(binding) { gradeDialogSubject.text = grade.subject - gradeDialogWeightValue.text = grade.weight - gradeDialogWeightLayout.backgroundTintList = - ColorStateList.valueOf(requireContext().getCompatColor(grade.getGradeColor())) + gradeDialogColorAndWeightValue.run { + text = context.getString(R.string.grade_weight_value, grade.weight) + setBackgroundResource(grade.getGradeColor()) + } gradeDialogDateValue.text = grade.date.toFormattedString() gradeDialogColorValue.text = getString(grade.colorStringId) @@ -71,12 +74,7 @@ class GradeDetailsDialog : BaseDialogFragment() { gradeDialogValue.run { text = grade.entry - backgroundTintList = ColorStateList.valueOf( - ContextCompat.getColor( - requireContext(), - grade.getBackgroundColor(gradeColorTheme) - ) - ) + setBackgroundResource(grade.getBackgroundColor(gradeColorTheme)) } gradeDialogTeacherValue.text = grade.teacher.ifBlank { getString(R.string.all_no_data) } 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 23d767a6f..81f3226ad 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt @@ -5,7 +5,9 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View -import android.view.View.* +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -40,7 +42,6 @@ class GradeDetailsFragment : override val isViewEmpty get() = gradeDetailsAdapter.itemCount == 0 - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt index e5f1eba0c..fd0ac5471 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt @@ -22,8 +22,6 @@ import io.github.wulkanowy.databinding.ItemGradeStatisticsHeaderBinding import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding import io.github.wulkanowy.utils.getThemeAttrColor import javax.inject.Inject -import kotlin.math.max -import kotlin.math.roundToInt class GradeStatisticsAdapter @Inject constructor() : RecyclerView.Adapter() { @@ -118,9 +116,7 @@ class GradeStatisticsAdapter @Inject constructor() : } ) - binding.gradeStatisticsTypeSwitch.addOnButtonCheckedListener { _, checkedId, isChecked -> - if (!isChecked) return@addOnButtonCheckedListener - + binding.gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId -> currentDataType = when (checkedId) { R.id.gradeStatisticsTypePartial -> GradeStatisticsItem.DataType.PARTIAL R.id.gradeStatisticsTypeSemester -> GradeStatisticsItem.DataType.SEMESTER @@ -271,7 +267,7 @@ class GradeStatisticsAdapter @Inject constructor() : valueTextSize = 12f valueTextColor = binding.root.context.getThemeAttrColor(android.R.attr.textColorPrimary) valueFormatter = object : ValueFormatter() { - override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}" + override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}%" } colors = gradePointsColors } @@ -306,20 +302,15 @@ class GradeStatisticsAdapter @Inject constructor() : } xAxis.setDrawLabels(false) xAxis.setDrawGridLines(false) - - val yMaxFromValues = (max(points.others, points.student)).roundToInt() + 30f - val yMaxFromValuesWithMargin = ((yMaxFromValues / 10.0).roundToInt() * 10).toFloat() - val yMax = yMaxFromValuesWithMargin.coerceAtLeast(100f) - val yLabelCount = (yMax / 10).toInt() + 1 with(axisLeft) { axisMinimum = 0f - axisMaximum = yMax - labelCount = yLabelCount + axisMaximum = 100f + labelCount = 11 } with(axisRight) { axisMinimum = 0f - axisMaximum = yMax - labelCount = yLabelCount + axisMaximum = 100f + labelCount = 11 } invalidate() } 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 edc384c5e..2af59c011 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 @@ -15,7 +15,6 @@ import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeView import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.serializable import io.github.wulkanowy.utils.setOnItemSelectedListener import javax.inject.Inject @@ -49,8 +48,8 @@ class GradeStatisticsFragment : messageContainer = binding.gradeStatisticsRecycler presenter.onAttachView( view = this, - type = savedInstanceState?.serializable(SAVED_CHART_TYPE), - subjectName = savedInstanceState?.serializable(SAVED_SUBJECT_NAME), + type = savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType, + subjectName = savedInstanceState?.getSerializable(SAVED_SUBJECT_NAME) as? String, ) } 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 8dcade56e..082c847e5 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,6 @@ import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.databinding.ItemGradeSummaryBinding import io.github.wulkanowy.databinding.ScrollableHeaderGradeSummaryBinding -import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid import io.github.wulkanowy.utils.calcFinalAverage import java.util.Locale import javax.inject.Inject @@ -62,7 +61,7 @@ class GradeSummaryAdapter @Inject constructor( if (items.isEmpty()) return val context = binding.root.context - val finalItemsCount = items.count { isGradeValid(it.finalGrade) } + val finalItemsCount = items.count { it.finalGrade.matches("[0-6][+-]?".toRegex()) } val calculatedItemsCount = items.count { value -> value.average != 0.0 } val allItemsCount = items.count { !it.subject.equals("zachowanie", true) } val finalAverage = items.calcFinalAverage( 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 abd0b13c4..3810902ff 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 @@ -7,7 +7,6 @@ import android.view.View.INVISIBLE import android.view.View.VISIBLE import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.GradeSummary @@ -119,7 +118,7 @@ class GradeSummaryFragment : } override fun showCalculatedAverageHelpDialog() { - MaterialAlertDialogBuilder(requireContext()) + 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) { _, _ -> } @@ -127,7 +126,7 @@ class GradeSummaryFragment : } override fun showFinalAverageHelpDialog() { - MaterialAlertDialogBuilder(requireContext()) + 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) { _, _ -> } 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 32508ff6f..4d5a43d8f 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 @@ -42,7 +42,7 @@ class GradeSummaryPresenter @Inject constructor( val student = studentRepository.getCurrentStudent() averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh) } - .logResourceStatus("load grade summary") + .logResourceStatus("load grade summary", showData = true) .mapResourceData { createGradeSummaryItems(it) } .onResourceData { view?.run { 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 0381acf35..d4eaade2c 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 @@ -21,7 +21,7 @@ import javax.inject.Inject @AndroidEntryPoint class HomeworkFragment : BaseFragment(R.layout.fragment_homework), - HomeworkView, MainView.TitledView, MainView.MainChildView { + HomeworkView, MainView.TitledView { @Inject lateinit var presenter: HomeworkPresenter @@ -67,7 +67,7 @@ class HomeworkFragment : BaseFragment(R.layout.fragment openAddHomeworkButton.setOnClickListener { presenter.onHomeworkAddButtonClicked() } - homeworkNavContainer.elevation = requireContext().dpToPx(3f) + homeworkNavContainer.elevation = requireContext().dpToPx(8f) } } @@ -133,14 +133,6 @@ class HomeworkFragment : BaseFragment(R.layout.fragment (activity as? MainActivity)?.showDialogFragment(HomeworkAddDialog()) } - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onViewReselected() - } - - override fun resetView() { - binding.homeworkRecycler.smoothScrollToPosition(0) - } - override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt index 6b263e26d..2ac552b41 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 @@ -177,21 +177,8 @@ class HomeworkPresenter @Inject constructor( showNextButton(!currentDate.plusDays(7).isHolidays) updateNavigationWeek( "${currentDate.monday.toFormattedString("dd.MM")} - " + - currentDate.sunday.toFormattedString("dd.MM") + currentDate.sunday.toFormattedString("dd.MM") ) } } - - fun onViewReselected() { - Timber.i("Homework view is reselected") - - baseDate = LocalDate.now().nextOrSameSchoolDay - - if (currentDate != baseDate) { - reloadView(baseDate) - loadData() - } else if (view?.isViewEmpty == false) { - view?.resetView() - } - } } 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 56ba6c89e..7c05ab865 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 @@ -36,6 +36,4 @@ interface HomeworkView : BaseView { fun showHomeworkDialog(homework: Homework) fun showAddHomeworkDialog() - - fun resetView() } 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 index c51370ea7..c2aff2b13 100644 --- 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 @@ -1,10 +1,10 @@ package io.github.wulkanowy.ui.modules.homework.add -import android.app.Dialog 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.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.databinding.DialogHomeworkAddBinding @@ -21,15 +21,20 @@ class HomeworkAddDialog : BaseDialogFragment(), Homewo @Inject lateinit var presenter: HomeworkAddPresenter - //todo: move it to presenter + // todo: move it to presenter private var date: LocalDate? = null - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogHomeworkAddBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() + override fun 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) 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 1ad2a0e32..e03707a5c 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 @@ -31,8 +31,14 @@ class HomeworkDetailsAdapter @Inject constructor() : attachments = value?.attachments.orEmpty() } + var isHomeworkFullscreen = false + var onAttachmentClickListener: (url: String) -> Unit = {} + var onFullScreenClickListener = {} + + var onFullScreenExitClickListener = {} + var onDeleteClickListener: (homework: Homework) -> Unit = {} override fun getItemCount() = 1 + if (attachments.isNotEmpty()) attachments.size + 1 else 0 @@ -76,6 +82,18 @@ class HomeworkDetailsAdapter @Inject constructor() : homeworkDialogTeacher.text = homework?.teacher.ifNullOrBlank { noDataString } homeworkDialogContent.text = homework?.content.ifNullOrBlank { noDataString } homeworkDialogDelete.visibility = if (homework?.isAddedByUser == true) VISIBLE else GONE + homeworkDialogFullScreen.visibility = if (isHomeworkFullscreen) GONE else VISIBLE + homeworkDialogFullScreenExit.visibility = if (isHomeworkFullscreen) VISIBLE else GONE + homeworkDialogFullScreen.setOnClickListener { + homeworkDialogFullScreen.visibility = GONE + homeworkDialogFullScreenExit.visibility = VISIBLE + onFullScreenClickListener() + } + homeworkDialogFullScreenExit.setOnClickListener { + homeworkDialogFullScreen.visibility = VISIBLE + homeworkDialogFullScreenExit.visibility = GONE + onFullScreenExitClickListener() + } 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 1f9bc881b..f9d463510 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt @@ -1,19 +1,19 @@ package io.github.wulkanowy.ui.modules.homework.details import android.annotation.SuppressLint -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf +import android.view.ViewGroup +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.databinding.DialogHomeworkBinding import io.github.wulkanowy.ui.base.BaseDialogFragment import io.github.wulkanowy.utils.openInternetBrowser -import io.github.wulkanowy.utils.serializable import javax.inject.Inject @AndroidEntryPoint @@ -35,20 +35,23 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew private const val ARGUMENT_KEY = "Item" fun newInstance(homework: Homework) = HomeworkDetailsDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to homework) + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - homework = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + homework = getSerializable(ARGUMENT_KEY) as Homework + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogHomeworkBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogHomeworkBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -64,11 +67,26 @@ class HomeworkDetailsDialog : BaseDialogFragment(), Homew homeworkDialogClose.setOnClickListener { dismiss() } } + if (presenter.isHomeworkFullscreen) { + dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT) + } else { + dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) + } + with(binding.homeworkDialogRecycler) { layoutManager = LinearLayoutManager(context) adapter = detailsAdapter.apply { onAttachmentClickListener = { context.openInternetBrowser(it, ::showMessage) } + onFullScreenClickListener = { + dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT) + presenter.isHomeworkFullscreen = true + } + onFullScreenExitClickListener = { + dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) + presenter.isHomeworkFullscreen = false + } onDeleteClickListener = { homework -> presenter.deleteHomework(homework) } + isHomeworkFullscreen = presenter.isHomeworkFullscreen homework = this@HomeworkDetailsDialog.homework } } 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 84933f06b..e76df6bd0 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 @@ -5,6 +5,7 @@ import io.github.wulkanowy.data.logResourceStatus import io.github.wulkanowy.data.onResourceError import io.github.wulkanowy.data.onResourceSuccess import io.github.wulkanowy.data.repositories.HomeworkRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter @@ -18,8 +19,15 @@ class HomeworkDetailsPresenter @Inject constructor( studentRepository: StudentRepository, private val homeworkRepository: HomeworkRepository, private val analytics: AnalyticsHelper, + private val preferencesRepository: PreferencesRepository ) : BasePresenter(errorHandler, studentRepository) { + var isHomeworkFullscreen + get() = preferencesRepository.isHomeworkFullscreen + set(value) { + preferencesRepository.isHomeworkFullscreen = value + } + override fun onAttachView(view: HomeworkDetailsView) { super.onAttachView(view) view.initView() 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 88f295788..aac60b56d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt @@ -2,17 +2,13 @@ package io.github.wulkanowy.ui.modules.login import android.content.Context import android.content.Intent -import android.content.pm.PackageManager -import android.os.Build.VERSION_CODES.TIRAMISU import android.os.Bundle import android.view.MenuItem -import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE import androidx.fragment.app.commit import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.ActivityLoginBinding import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment @@ -20,10 +16,7 @@ import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.notifications.NotificationsFragment -import io.github.wulkanowy.utils.AppInfo -import io.github.wulkanowy.utils.InAppUpdateHelper +import io.github.wulkanowy.utils.UpdateHelper import javax.inject.Inject @AndroidEntryPoint @@ -33,10 +26,7 @@ class LoginActivity : BaseActivity(), Logi override lateinit var presenter: LoginPresenter @Inject - lateinit var inAppUpdateHelper: InAppUpdateHelper - - @Inject - lateinit var appInfo: AppInfo + lateinit var updateHelper: UpdateHelper companion object { fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java) @@ -47,10 +37,10 @@ class LoginActivity : BaseActivity(), Logi setContentView(ActivityLoginBinding.inflate(layoutInflater).apply { binding = this }.root) setSupportActionBar(binding.loginToolbar) messageContainer = binding.loginContainer - inAppUpdateHelper.messageContainer = binding.loginContainer + updateHelper.messageContainer = binding.loginContainer presenter.onAttachView(this) - inAppUpdateHelper.checkAndInstallUpdates() + updateHelper.checkAndInstallUpdates(this) if (savedInstanceState == null) { openFragment(LoginFormFragment.newInstance(), clearBackStack = true) @@ -65,7 +55,7 @@ class LoginActivity : BaseActivity(), Logi } override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) onBackPressedDispatcher.onBackPressed() + if (item.itemId == android.R.id.home) onBackPressed() return true } @@ -77,24 +67,8 @@ class LoginActivity : BaseActivity(), Logi openFragment(LoginSymbolFragment.newInstance(loginData)) } - fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) { - openFragment(LoginStudentSelectFragment.newInstance(loginData, registerUser)) - } - - fun navigateToNotifications() { - val isNotificationsPermissionRequired = appInfo.systemVersion >= TIRAMISU - val isPermissionGranted = ContextCompat.checkSelfPermission( - this, "android.permission.POST_NOTIFICATIONS" - ) == PackageManager.PERMISSION_GRANTED - - if (isNotificationsPermissionRequired && !isPermissionGranted) { - openFragment(NotificationsFragment.newInstance(), clearBackStack = true) - } else navigateToFinish() - } - - fun navigateToFinish() { - startActivity(MainActivity.getStartIntent(this)) - finish() + fun navigateToStudentSelect(studentsWithSemesters: List) { + openFragment(LoginStudentSelectFragment.newInstance(studentsWithSemesters)) } fun onAdvancedLoginClick() { @@ -106,8 +80,6 @@ class LoginActivity : BaseActivity(), Logi } private fun openFragment(fragment: Fragment, clearBackStack: Boolean = false) { - supportFragmentManager.popBackStack(fragment::class.java.name, POP_BACK_STACK_INCLUSIVE) - supportFragmentManager.commit { replace(R.id.loginContainer, fragment) setReorderingAllowed(true) @@ -117,6 +89,14 @@ class LoginActivity : BaseActivity(), Logi override fun onResume() { super.onResume() - inAppUpdateHelper.onResume() + updateHelper.onResume(this) + } + + //https://developer.android.com/guide/playcore/in-app-updates#status_callback + @Deprecated("Deprecated in Java") + @Suppress("DEPRECATION") + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + updateHelper.onActivityResult(requestCode, resultCode) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt index 2c11bb6d5..5d4743589 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt @@ -6,6 +6,4 @@ data class LoginData( val login: String, val password: String, val baseUrl: String, - val domainSuffix: String, - val symbol: String?, ) : Serializable diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt index 4f709438a..37ab71dce 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 @@ -4,15 +4,13 @@ import android.content.Context import android.database.sqlite.SQLiteConstraintException import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R -import io.github.wulkanowy.sdk.hebe.exception.InvalidPinException -import io.github.wulkanowy.sdk.hebe.exception.InvalidTokenException -import io.github.wulkanowy.sdk.hebe.exception.TokenDeadException -import io.github.wulkanowy.sdk.hebe.exception.UnknownTokenException +import io.github.wulkanowy.sdk.mobile.exception.InvalidPinException +import io.github.wulkanowy.sdk.mobile.exception.InvalidSymbolException +import io.github.wulkanowy.sdk.mobile.exception.InvalidTokenException +import io.github.wulkanowy.sdk.mobile.exception.TokenDeadException import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject -import io.github.wulkanowy.sdk.hebe.exception.InvalidSymbolException as InvalidHebeSymbolException -import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException as InvalidScrapperSymbolException class LoginErrorHandler @Inject constructor( @ApplicationContext context: Context, @@ -34,11 +32,9 @@ class LoginErrorHandler @Inject constructor( is BadCredentialsException -> onBadCredentials(error.message) is SQLiteConstraintException -> onStudentDuplicate(resources.getString(R.string.login_duplicate_student)) is TokenDeadException -> onInvalidToken(resources.getString(R.string.login_expired_token)) - is UnknownTokenException, is InvalidTokenException -> onInvalidToken(resources.getString(R.string.login_invalid_token)) is InvalidPinException -> onInvalidPin(resources.getString(R.string.login_invalid_pin)) - is InvalidScrapperSymbolException, - is InvalidHebeSymbolException -> onInvalidSymbol(resources.getString(R.string.login_invalid_symbol)) + is InvalidSymbolException -> onInvalidSymbol(resources.getString(R.string.login_invalid_symbol)) else -> super.proceed(error) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt index 13d2c14a7..37dcb38b3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt @@ -5,11 +5,10 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.widget.ArrayAdapter -import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.FragmentLoginAdvancedBinding import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.ui.base.BaseFragment @@ -35,9 +34,9 @@ class LoginAdvancedFragment : override val formLoginType: String get() = when (binding.loginTypeSwitch.checkedRadioButtonId) { - R.id.loginTypeApi -> Sdk.Mode.HEBE.name - R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER.name - else -> Sdk.Mode.HYBRID.name + R.id.loginTypeApi -> "API" + R.id.loginTypeScrapper -> "SCRAPPER" + else -> "HYBRID" } override val formUsernameValue: String @@ -56,9 +55,6 @@ class LoginAdvancedFragment : get() = hostValues.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) .orEmpty() - override val formDomainSuffix: String - get() = binding.loginFormDomainSuffix.text.toString().trim() - override val formHostSymbol: String get() = hostSymbols.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) .orEmpty() @@ -103,7 +99,7 @@ class LoginAdvancedFragment : loginTypeSwitch.setOnCheckedChangeListener { _, checkedId -> presenter.onLoginModeSelected( when (checkedId) { - R.id.loginTypeApi -> Sdk.Mode.HEBE + R.id.loginTypeApi -> Sdk.Mode.API R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER else -> Sdk.Mode.HYBRID } @@ -283,7 +279,6 @@ class LoginAdvancedFragment : loginFormUsernameLayout.visibility = VISIBLE loginFormPassLayout.visibility = VISIBLE loginFormHostLayout.visibility = VISIBLE - loginFormDomainSuffixLayout.isVisible = true loginFormPinLayout.visibility = GONE loginFormSymbolLayout.visibility = VISIBLE loginFormTokenLayout.visibility = GONE @@ -295,7 +290,6 @@ class LoginAdvancedFragment : loginFormUsernameLayout.visibility = VISIBLE loginFormPassLayout.visibility = VISIBLE loginFormHostLayout.visibility = VISIBLE - loginFormDomainSuffixLayout.isVisible = true loginFormPinLayout.visibility = GONE loginFormSymbolLayout.visibility = VISIBLE loginFormTokenLayout.visibility = GONE @@ -307,7 +301,6 @@ class LoginAdvancedFragment : loginFormUsernameLayout.visibility = GONE loginFormPassLayout.visibility = GONE loginFormHostLayout.visibility = GONE - loginFormDomainSuffixLayout.isVisible = false loginFormPinLayout.visibility = VISIBLE loginFormSymbolLayout.visibility = VISIBLE loginFormTokenLayout.visibility = VISIBLE @@ -334,8 +327,8 @@ class LoginAdvancedFragment : (activity as? LoginActivity)?.navigateToSymbolFragment(loginData) } - override fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) { - (activity as? LoginActivity)?.navigateToStudentSelect(loginData, registerUser) + override fun navigateToStudentSelect(studentsWithSemesters: List) { + (activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters) } 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 a17ad0035..1b42c6c52 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt @@ -1,13 +1,12 @@ package io.github.wulkanowy.ui.modules.login.advanced import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.logResourceStatus import io.github.wulkanowy.data.onResourceNotLoading -import io.github.wulkanowy.data.pojos.RegisterUser import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.sdk.scrapper.getNormalizedSymbol import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginData import io.github.wulkanowy.ui.modules.login.LoginErrorHandler @@ -92,16 +91,14 @@ class LoginAdvancedPresenter @Inject constructor( fun onLoginModeSelected(type: Sdk.Mode) { view?.run { when (type) { - Sdk.Mode.HEBE -> { + Sdk.Mode.API -> { showOnlyMobileApiModeInputs() showMobileApiWarningMessage() } - Sdk.Mode.SCRAPPER -> { showOnlyScrapperModeInputs() showScraperWarningMessage() } - Sdk.Mode.HYBRID -> { showOnlyHybridModeInputs() showHybridWarningMessage() @@ -142,30 +139,23 @@ class LoginAdvancedPresenter @Inject constructor( showProgress(true) showContent(false) } - is Resource.Success -> { analytics.logEvent( "registration_form", - "success" to true, - "scrapperBaseUrl" to view?.formHostValue.orEmpty(), - "error" to "No error" - ) - val loginData = LoginData( - login = view?.formUsernameValue.orEmpty().trim(), - password = view?.formPassValue.orEmpty().trim(), - baseUrl = view?.formHostValue.orEmpty().trim(), - domainSuffix = view?.formDomainSuffix.orEmpty().trim(), - symbol = view?.formSymbolValue.orEmpty().trim().getNormalizedSymbol(), - ) - when (it.data.symbols.size) { - 0 -> view?.navigateToSymbol(loginData) - else -> view?.navigateToStudentSelect( - loginData = loginData, - registerUser = it.data, - ) - } + "success" to true, + "students" to it.data.size, + "error" to "No error" + ) + val loginData = LoginData( + login = view?.formUsernameValue.orEmpty().trim(), + password = view?.formPassValue.orEmpty().trim(), + baseUrl = view?.formHostValue.orEmpty().trim() + ) + when (it.data.size) { + 0 -> view?.navigateToSymbol(loginData) + else -> view?.navigateToStudentSelect(it.data) + } } - is Resource.Error -> { analytics.logEvent( "registration_form", @@ -183,22 +173,20 @@ class LoginAdvancedPresenter @Inject constructor( }.launch("login") } - private suspend fun getStudentsAppropriatesToLoginType(): RegisterUser { + private suspend fun getStudentsAppropriatesToLoginType(): List { val email = view?.formUsernameValue.orEmpty() val password = view?.formPassValue.orEmpty() val endpoint = view?.formHostValue.orEmpty() - val domainSuffix = view?.formDomainSuffix.orEmpty() val pin = view?.formPinValue.orEmpty() val symbol = view?.formSymbolValue.orEmpty() val token = view?.formTokenValue.orEmpty() return when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) { - Sdk.Mode.HEBE -> studentRepository.getStudentsApi(pin, symbol, token) - Sdk.Mode.SCRAPPER -> studentRepository.getUserSubjectsFromScrapper( - email, password, endpoint, domainSuffix, symbol + Sdk.Mode.API -> studentRepository.getStudentsApi(pin, symbol, token) + Sdk.Mode.SCRAPPER -> studentRepository.getStudentsScrapper( + email, password, endpoint, symbol ) - Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid( email, password, endpoint, symbol ) @@ -217,8 +205,8 @@ class LoginAdvancedPresenter @Inject constructor( var isCorrect = true - when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) { - Sdk.Mode.HEBE -> { + when (Sdk.Mode.valueOf(view?.formLoginType ?: "")) { + Sdk.Mode.API -> { if (pin.isEmpty()) { view?.setErrorPinRequired() isCorrect = false @@ -234,17 +222,17 @@ class LoginAdvancedPresenter @Inject constructor( isCorrect = false } } - Sdk.Mode.HYBRID, Sdk.Mode.SCRAPPER -> { if (login.isEmpty()) { view?.setErrorUsernameRequired() isCorrect = false } else { - if ("@" in login && "login" in host) { + if ("@" in login && "standard" !in host) { view?.setErrorLoginRequired() isCorrect = false } - if ("@" !in login && "email" in host) { + + if ("@" !in login && "standard" in host) { view?.setErrorEmailRequired() isCorrect = false } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt index afd33e3b6..f9b84f1ab 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.login.advanced -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView import io.github.wulkanowy.ui.modules.login.LoginData @@ -12,8 +12,6 @@ interface LoginAdvancedView : BaseView { val formHostValue: String - val formDomainSuffix: String - val formHostSymbol: String val formLoginType: String @@ -74,7 +72,7 @@ interface LoginAdvancedView : BaseView { fun navigateToSymbol(loginData: LoginData) - fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) + fun navigateToStudentSelect(studentsWithSemesters: List) fun setErrorPinRequired() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt index 8e9b86fa3..d31f5cf0f 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 @@ -9,17 +9,17 @@ import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.AdminMessage -import io.github.wulkanowy.data.pojos.RegisterUser -import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.FragmentLoginFormBinding import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginData -import io.github.wulkanowy.ui.modules.login.support.LoginSupportDialog -import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.openEmailClient +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.setOnEditorDoneSignIn +import io.github.wulkanowy.utils.showSoftInput import javax.inject.Inject @AndroidEntryPoint @@ -32,9 +32,6 @@ class LoginFormFragment : BaseFragment(R.layout.fragme @Inject lateinit var appInfo: AppInfo - @Inject - lateinit var preferencesRepository: PreferencesRepository - companion object { fun newInstance() = LoginFormFragment() } @@ -49,9 +46,6 @@ class LoginFormFragment : BaseFragment(R.layout.fragme get() = hostValues.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) .orEmpty() - override val formDomainSuffix: String - get() = binding.loginFormDomainSuffix.text.toString() - override val formHostSymbol: String get() = hostSymbols.getOrNull(hostKeys.indexOf(binding.loginFormHost.text.toString())) .orEmpty() @@ -151,14 +145,12 @@ class LoginFormFragment : BaseFragment(R.layout.fragme override fun setErrorPassRequired(focus: Boolean) { with(binding.loginFormPassLayout) { error = getString(R.string.error_field_required) - setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError)) } } override fun setErrorPassInvalid(focus: Boolean) { with(binding.loginFormPassLayout) { error = getString(R.string.login_invalid_password) - setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError)) } } @@ -166,7 +158,6 @@ class LoginFormFragment : BaseFragment(R.layout.fragme with(binding) { loginFormUsernameLayout.error = " " loginFormPassLayout.error = " " - loginFormPassLayout.setEndIconTintList(requireContext().getAttrColorStateList(R.attr.colorError)) loginFormHostLayout.error = " " loginFormErrorBox.text = message ?: getString(R.string.login_incorrect_password_default) loginFormErrorBox.isVisible = true @@ -186,9 +177,6 @@ class LoginFormFragment : BaseFragment(R.layout.fragme override fun clearPassError() { binding.loginFormPassLayout.error = null - binding.loginFormPassLayout.setEndIconTintList( - requireContext().getAttrColorStateList(R.attr.colorOnSurface) - ) binding.loginFormErrorBox.isVisible = false } @@ -213,34 +201,14 @@ class LoginFormFragment : BaseFragment(R.layout.fragme binding.loginFormContainer.visibility = if (show) VISIBLE else GONE } - override fun showAdminMessage(message: AdminMessage?) { - AdminMessageViewHolder( - binding = binding.loginFormMessage, - onAdminMessageDismissClickListener = presenter::onAdminMessageDismissed, - onAdminMessageClickListener = presenter::onAdminMessageSelected, - ).bind(message) - binding.loginFormMessage.root.isVisible = message != null - } - - override fun openInternetBrowser(url: String) { - requireContext().openInternetBrowser(url) - } - - override fun showDomainSuffixInput(show: Boolean) { - binding.loginFormDomainSuffixLayout.isVisible = show - } - - override fun showOtherOptionsButton(show: Boolean) { - binding.loginFormAdvancedButton.isVisible = show - } - @SuppressLint("SetTextI18n") override fun showVersion() { binding.loginFormVersion.text = "v${appInfo.versionName}" } override fun showContact(show: Boolean) { - binding.loginFormContact.isVisible = show + binding.loginFormContact.visibility = if (show) VISIBLE else GONE + binding.loginFormRecoverLink.visibility = if (show) GONE else VISIBLE } override fun openPrivacyPolicyPage() { @@ -254,8 +222,8 @@ class LoginFormFragment : BaseFragment(R.layout.fragme (activity as? LoginActivity)?.navigateToSymbolFragment(loginData) } - override fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) { - (activity as? LoginActivity)?.navigateToStudentSelect(loginData, registerUser) + override fun navigateToStudentSelect(studentsWithSemesters: List) { + (activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters) } override fun openAdvancedLogin() { @@ -281,10 +249,21 @@ class LoginFormFragment : BaseFragment(R.layout.fragme override fun onResume() { super.onResume() presenter.updateUsernameLabel() - presenter.updateCustomDomainSuffixVisibility() } - override fun openEmail(supportInfo: LoginSupportInfo) { - LoginSupportDialog.newInstance(supportInfo).show(childFragmentManager, "support_dialog") + override fun openEmail(lastError: String) { + context?.openEmailClient( + chooserTitle = requireContext().getString(R.string.login_email_intent_title), + email = "wulkanowyinc@gmail.com", + subject = requireContext().getString(R.string.login_email_subject), + body = requireContext().getString( + R.string.login_email_text, + "${appInfo.systemManufacturer} ${appInfo.systemModel}", + appInfo.systemVersion.toString(), + appInfo.versionName, + "$formHostValue/$formHostSymbol", + lastError + ) + ) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt index ad535c382..0acb0ea6d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt @@ -1,25 +1,12 @@ package io.github.wulkanowy.ui.modules.login.form import androidx.core.net.toUri -import io.github.wulkanowy.data.db.entities.AdminMessage -import io.github.wulkanowy.data.enums.MessageType -import io.github.wulkanowy.data.flatResourceFlow -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.onResourceData -import io.github.wulkanowy.data.onResourceError -import io.github.wulkanowy.data.onResourceLoading -import io.github.wulkanowy.data.onResourceNotLoading -import io.github.wulkanowy.data.onResourceSuccess -import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.* import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow -import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginData import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo import io.github.wulkanowy.utils.AnalyticsHelper -import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.ifNullOrBlank import timber.log.Timber import java.net.URL @@ -28,10 +15,7 @@ import javax.inject.Inject class LoginFormPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val appInfo: AppInfo, - private val analytics: AnalyticsHelper, - private val getAppropriateAdminMessageUseCase: GetAppropriateAdminMessageUseCase, - private val preferencesRepository: PreferencesRepository, + private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { private var lastError: Throwable? = null @@ -41,7 +25,6 @@ class LoginFormPresenter @Inject constructor( view.run { initView() showContact(false) - showOtherOptionsButton(appInfo.isDebug) showVersion() loginErrorHandler.onBadCredentials = { @@ -50,31 +33,6 @@ class LoginFormPresenter @Inject constructor( Timber.i("Entered wrong username or password") } } - - reloadAdminMessage() - } - - private fun reloadAdminMessage() { - flatResourceFlow { - getAppropriateAdminMessageUseCase( - scrapperBaseUrl = view?.formHostValue.orEmpty(), - type = MessageType.LOGIN_MESSAGE, - ) - } - .logResourceStatus("load login admin message") - .onResourceData { view?.showAdminMessage(it) } - .onResourceError { view?.showAdminMessage(null) } - .launch() - } - - fun onAdminMessageSelected(url: String?) { - url?.let { view?.openInternetBrowser(it) } - } - - fun onAdminMessageDismissed(adminMessage: AdminMessage) { - preferencesRepository.dismissedAdminMessageIds += adminMessage.id - - view?.showAdminMessage(null) } fun onPrivacyLinkClick() { @@ -95,15 +53,7 @@ class LoginFormPresenter @Inject constructor( } else if (formUsernameValue == "jan@fakelog.cf" && formPassValue == "jan123") { setCredentials("", "") } - updateCustomDomainSuffixVisibility() updateUsernameLabel() - reloadAdminMessage() - } - } - - fun updateCustomDomainSuffixVisibility() { - view?.run { - showDomainSuffixInput("customSuffix" in formHostValue) } } @@ -134,36 +84,20 @@ class LoginFormPresenter @Inject constructor( } } - private fun getLoginData(): LoginData { + fun onSignInClick() { val email = view?.formUsernameValue.orEmpty().trim() val password = view?.formPassValue.orEmpty().trim() val host = view?.formHostValue.orEmpty().trim() - val domainSuffix = view?.formDomainSuffix.orEmpty().trim().takeIf { - "customSuffix" in host - }.orEmpty() val symbol = view?.formHostSymbol.orEmpty().trim() - return LoginData( - login = email, - password = password, - baseUrl = host, - domainSuffix = domainSuffix, - symbol = symbol - ) - } - - fun onSignInClick() { - val loginData = getLoginData() - - if (!validateCredentials(loginData.login, loginData.password, loginData.baseUrl)) return + if (!validateCredentials(email, password, host)) return resourceFlow { - studentRepository.getUserSubjectsFromScrapper( - email = loginData.login, - password = loginData.password, - scrapperBaseUrl = loginData.baseUrl, - domainSuffix = loginData.domainSuffix, - symbol = loginData.symbol.orEmpty(), + studentRepository.getStudentsScrapper( + email = email, + password = password, + scrapperBaseUrl = host, + symbol = symbol ) } .logResourceStatus("login") @@ -175,14 +109,15 @@ class LoginFormPresenter @Inject constructor( } } .onResourceSuccess { - when (it.symbols.size) { - 0 -> view?.navigateToSymbol(loginData) - else -> view?.navigateToStudentSelect(loginData, it) + when (it.size) { + 0 -> view?.navigateToSymbol(LoginData(email, password, host)) + else -> view?.navigateToStudentSelect(it) } analytics.logEvent( "registration_form", "success" to true, - "scrapperBaseUrl" to loginData.baseUrl, + "students" to it.size, + "scrapperBaseUrl" to host, "error" to "No error" ) } @@ -199,7 +134,8 @@ class LoginFormPresenter @Inject constructor( analytics.logEvent( "registration_form", "success" to false, - "scrapperBaseUrl" to loginData.baseUrl, + "students" to -1, + "scrapperBaseUrl" to host, "error" to it.message.ifNullOrBlank { "No message" } ) } @@ -211,14 +147,7 @@ class LoginFormPresenter @Inject constructor( } fun onEmailClick() { - view?.openEmail( - LoginSupportInfo( - loginData = getLoginData(), - lastErrorMessage = lastError?.message, - registerUser = null, - enteredSymbol = null, - ) - ) + view?.openEmail(lastError?.message.ifNullOrBlank { "none" }) } fun onRecoverClick() { 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 f2b7b1003..8003975db 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt @@ -1,10 +1,8 @@ package io.github.wulkanowy.ui.modules.login.form -import io.github.wulkanowy.data.db.entities.AdminMessage -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView import io.github.wulkanowy.ui.modules.login.LoginData -import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo interface LoginFormView : BaseView { @@ -16,8 +14,6 @@ interface LoginFormView : BaseView { val formHostValue: String - val formDomainSuffix: String - val formHostSymbol: String val nicknameLabel: String @@ -60,19 +56,11 @@ interface LoginFormView : BaseView { fun showContent(show: Boolean) - fun showAdminMessage(message: AdminMessage?) - - fun openInternetBrowser(url: String) - - fun showDomainSuffixInput(show: Boolean) - - fun showOtherOptionsButton(show: Boolean) - fun showVersion() fun navigateToSymbol(loginData: LoginData) - fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) + fun navigateToStudentSelect(studentsWithSemesters: List) fun openPrivacyPolicyPage() @@ -80,7 +68,7 @@ interface LoginFormView : BaseView { fun openFaqPage() - fun openEmail(supportInfo: LoginSupportInfo) + fun openEmail(lastError: String) fun openAdvancedLogin() 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 b9afba986..786bbfce8 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 @@ -98,7 +98,7 @@ class LoginRecoverFragment : loginRecoverButton.setOnClickListener { presenter.onRecoverClick() } loginRecoverErrorRetry.setOnClickListener { presenter.onRecoverClick() } loginRecoverErrorDetails.setOnClickListener { presenter.onDetailsClick() } - loginRecoverLogin.setOnClickListener { (activity as LoginActivity).onBackPressedDispatcher.onBackPressed() } + loginRecoverLogin.setOnClickListener { (activity as LoginActivity).onBackPressed() } } with(bindingLocal.loginRecoverHost) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt index a424df40d..3d0493012 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt @@ -46,7 +46,7 @@ class LoginRecoverPresenter @Inject constructor( fun updateFields() { view?.run { - setUsernameHint(if ("email" in recoverHostValue) emailHintString else loginPeselEmailHintString) + setUsernameHint(if ("standard" in recoverHostValue) emailHintString else loginPeselEmailHintString) } } @@ -92,7 +92,7 @@ class LoginRecoverPresenter @Inject constructor( isCorrect = false } - if ("email" in host && "@" !in username) { + if ("standard" in host && "@" !in username) { view?.setUsernameError(view?.invalidEmailString.orEmpty()) isCorrect = false } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt index e6d131829..c046c2ff5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectAdapter.kt @@ -2,182 +2,65 @@ package io.github.wulkanowy.ui.modules.login.studentselect import android.annotation.SuppressLint import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.recyclerview.widget.DiffUtil.ItemCallback -import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView -import io.github.wulkanowy.R -import io.github.wulkanowy.databinding.* +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.databinding.ItemLoginStudentSelectBinding import javax.inject.Inject -@SuppressLint("SetTextI18n") class LoginStudentSelectAdapter @Inject constructor() : - ListAdapter(Differ) { + RecyclerView.Adapter() { - override fun getItemViewType(position: Int): Int = getItem(position).type.ordinal + private val checkedList = mutableMapOf() - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - val inflater = LayoutInflater.from(parent.context) - return when (LoginStudentSelectItemType.values()[viewType]) { - LoginStudentSelectItemType.EMPTY_SYMBOLS_HEADER -> EmptySymbolsHeaderViewHolder( - ItemLoginStudentSelectEmptySymbolHeaderBinding.inflate(inflater, parent, false), - ) - LoginStudentSelectItemType.SYMBOL_HEADER -> SymbolsHeaderViewHolder( - ItemLoginStudentSelectHeaderSymbolBinding.inflate(inflater, parent, false) - ) - LoginStudentSelectItemType.SCHOOL_HEADER -> SchoolHeaderViewHolder( - ItemLoginStudentSelectHeaderSchoolBinding.inflate(inflater, parent, false) - ) - LoginStudentSelectItemType.STUDENT -> StudentViewHolder( - ItemLoginStudentSelectStudentBinding.inflate(inflater, parent, false) - ) - LoginStudentSelectItemType.HELP -> HelpViewHolder( - ItemLoginStudentSelectHelpBinding.inflate(inflater, parent, false) - ) + var items = emptyList>() + set(value) { + field = value + checkedList.clear() } - } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - when (holder) { - is EmptySymbolsHeaderViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.EmptySymbolsHeader) - is SymbolsHeaderViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.SymbolHeader) - is SchoolHeaderViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.SchoolHeader) - is StudentViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.Student) - is HelpViewHolder -> holder.bind(getItem(position) as LoginStudentSelectItem.Help) - } - } + var onClickListener: (StudentWithSemesters, alreadySaved: Boolean) -> Unit = { _, _ -> } - private class EmptySymbolsHeaderViewHolder( - private val binding: ItemLoginStudentSelectEmptySymbolHeaderBinding, - ) : RecyclerView.ViewHolder(binding.root) { + override fun getItemCount() = items.size - fun bind(item: LoginStudentSelectItem.EmptySymbolsHeader) { - with(binding) { - loginStudentSelectEmptySymbolChevron.rotation = if (item.isExpanded) 270f else 90f - root.setOnClickListener { item.onClick() } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemLoginStudentSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val (studentAndSemesters, alreadySaved) = items[position] + val student = studentAndSemesters.student + val semesters = studentAndSemesters.semesters + val diary = semesters.maxByOrNull { it.semesterId } + + with(holder.binding) { + loginItemName.text = "${student.studentName} ${diary?.diaryName.orEmpty()}" + loginItemSchool.text = student.schoolName + loginItemName.isEnabled = !alreadySaved + loginItemSchool.isEnabled = !alreadySaved + loginItemSignedIn.visibility = if (alreadySaved) View.VISIBLE else View.GONE + + with(loginItemCheck) { + isEnabled = !alreadySaved + keyListener = null + isChecked = checkedList[position] ?: false } - } - } - private class SymbolsHeaderViewHolder( - private val binding: ItemLoginStudentSelectHeaderSymbolBinding, - ) : RecyclerView.ViewHolder(binding.root) { - - fun bind(item: LoginStudentSelectItem.SymbolHeader) { - with(binding) { - loginStudentSelectHeaderSymbolValue.text = buildString { - append(root.context.getString(R.string.mobile_device_symbol)) - append(": ") - append(item.humanReadableName ?: item.symbol.symbol) - if (!item.humanReadableName.isNullOrBlank()) { - append(" (${item.symbol.symbol})") - } - } - loginStudentSelectHeaderSymbolUsername.text = item.symbol.userName - loginStudentSelectHeaderSymbolUsername.isVisible = item.symbol.userName.isNotBlank() - loginStudentSelectHeaderSymbolError.text = item.symbol.error?.message - loginStudentSelectHeaderSymbolError.isVisible = item.symbol.error != null - loginStudentSelectHeaderSymbolError.maxLines = when { - item.isErrorExpanded -> Int.MAX_VALUE - else -> 2 - } - - if (item.symbol.error != null) { - root.setOnClickListener { item.onClick(item.symbol) } - } else root.setOnClickListener(null) - } - } - } - - private class SchoolHeaderViewHolder( - private val binding: ItemLoginStudentSelectHeaderSchoolBinding, - ) : RecyclerView.ViewHolder(binding.root) { - - fun bind(item: LoginStudentSelectItem.SchoolHeader) { - with(binding) { - loginStudentSelectHeaderSchoolName.text = buildString { - append(item.unit.schoolName.trim()) - append(" (") - append(item.unit.schoolShortName) - append(")") - } - loginStudentSelectHeaderSchoolDetails.isVisible = item.unit.students.isEmpty() - loginStudentSelectHeaderSchoolError.text = item.unit.error?.message - loginStudentSelectHeaderSchoolError.isVisible = item.unit.error != null - loginStudentSelectHeaderSchoolError.maxLines = when { - item.isErrorExpanded -> Int.MAX_VALUE - else -> 2 - } - - if (item.unit.error != null) { - root.setOnClickListener { item.onClick(item.unit) } - } else root.setOnClickListener(null) - } - } - } - - private class StudentViewHolder( - private val binding: ItemLoginStudentSelectStudentBinding, - ) : RecyclerView.ViewHolder(binding.root) { - - fun bind(item: LoginStudentSelectItem.Student) { - val student = item.student - val semesters = student.semesters - val diary = semesters.maxByOrNull { it.semesterId } - - with(binding) { - loginItemName.text = "${student.studentName} ${student.studentSurname}" - loginItemName.isEnabled = item.isEnabled - loginItemSignedIn.text = if (!item.isEnabled) { - root.context.getString(R.string.login_signed_in) - } else diary?.diaryName + root.setOnClickListener { + onClickListener(studentAndSemesters, alreadySaved) with(loginItemCheck) { - keyListener = null - isEnabled = item.isEnabled - isChecked = item.isSelected || !item.isEnabled - } - - root.isEnabled = item.isEnabled - root.setOnClickListener { - item.onClick(item) + if (isEnabled) { + isChecked = !isChecked + checkedList[position] = isChecked + } } } } } - private class HelpViewHolder( - private val binding: ItemLoginStudentSelectHelpBinding, - ) : RecyclerView.ViewHolder(binding.root) { - - fun bind(item: LoginStudentSelectItem.Help) { - with(binding) { - loginStudentSelectHelpSymbol.isVisible = item.isSymbolButtonVisible - loginStudentSelectHelpSymbol.setOnClickListener { item.onEnterSymbolClick() } - loginStudentSelectHelpMail.setOnClickListener { item.onContactUsClick() } - loginStudentSelectHelpDiscord.setOnClickListener { item.onDiscordClick() } - } - } - } - - private object Differ : ItemCallback() { - - override fun areItemsTheSame( - oldItem: LoginStudentSelectItem, newItem: LoginStudentSelectItem - ): Boolean = when { - oldItem is LoginStudentSelectItem.EmptySymbolsHeader && newItem is LoginStudentSelectItem.EmptySymbolsHeader -> true - oldItem is LoginStudentSelectItem.SymbolHeader && newItem is LoginStudentSelectItem.SymbolHeader -> { - oldItem.symbol == newItem.symbol - } - oldItem is LoginStudentSelectItem.Student && newItem is LoginStudentSelectItem.Student -> { - oldItem.student == newItem.student - } - else -> oldItem == newItem - } - - override fun areContentsTheSame( - oldItem: LoginStudentSelectItem, newItem: LoginStudentSelectItem - ): Boolean = oldItem == newItem - } + class ItemViewHolder(val binding: ItemLoginStudentSelectBinding) : + RecyclerView.ViewHolder(binding.root) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt index 06efd8d98..6c910fe03 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt @@ -2,23 +2,20 @@ package io.github.wulkanowy.ui.modules.login.studentselect import android.os.Bundle import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE import androidx.core.os.bundleOf -import androidx.core.view.isVisible +import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.pojos.RegisterUser -import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.ui.modules.login.LoginData -import io.github.wulkanowy.ui.modules.login.support.LoginSupportDialog -import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo +import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.openEmailClient import io.github.wulkanowy.utils.openInternetBrowser -import io.github.wulkanowy.utils.serializable import javax.inject.Inject @AndroidEntryPoint @@ -35,86 +32,91 @@ class LoginStudentSelectFragment : @Inject lateinit var appInfo: AppInfo - @Inject - lateinit var preferencesRepository: PreferencesRepository - - private lateinit var symbolsNames: Array - private lateinit var symbolsValues: Array - - override val symbols: Map by lazy { - symbolsValues.zip(symbolsNames).toMap() - } - companion object { - private const val ARG_LOGIN = "LOGIN" - private const val ARG_STUDENTS = "STUDENTS" + const val ARG_STUDENTS = "STUDENTS" - fun newInstance(loginData: LoginData, registerUser: RegisterUser) = + fun newInstance(studentsWithSemesters: List) = LoginStudentSelectFragment().apply { - arguments = bundleOf( - ARG_LOGIN to loginData, - ARG_STUDENTS to registerUser, - ) + arguments = bundleOf(ARG_STUDENTS to studentsWithSemesters) } } + @Suppress("UNCHECKED_CAST") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentLoginStudentSelectBinding.bind(view) - - symbolsNames = resources.getStringArray(R.array.symbols) - symbolsValues = resources.getStringArray(R.array.symbols_values) - presenter.onAttachView( view = this, - loginData = requireArguments().serializable(ARG_LOGIN), - registerUser = requireArguments().serializable(ARG_STUDENTS), + students = requireArguments().getSerializable(ARG_STUDENTS) as List, ) } override fun initView() { (requireActivity() as LoginActivity).showActionBar(true) + loginAdapter.onClickListener = presenter::onItemSelected + with(binding) { loginStudentSelectSignIn.setOnClickListener { presenter.onSignIn() } - loginStudentSelectRecycler.adapter = loginAdapter + loginStudentSelectContactDiscord.setOnClickListener { presenter.onDiscordClick() } + loginStudentSelectContactEmail.setOnClickListener { presenter.onEmailClick() } + + with(loginStudentSelectRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = loginAdapter + } } } - override fun updateData(data: List) { - loginAdapter.submitList(data) + override fun updateData(data: List>) { + with(loginAdapter) { + items = data + notifyDataSetChanged() + } } - override fun navigateToSymbol(loginData: LoginData) { - (requireActivity() as LoginActivity).navigateToSymbolFragment(loginData) - } - - override fun navigateToNext() { - (requireActivity() as LoginActivity).navigateToNotifications() + override fun openMainView() { + startActivity(MainActivity.getStartIntent(requireContext())) + requireActivity().finish() } override fun showProgress(show: Boolean) { - binding.loginStudentSelectProgress.isVisible = show + binding.loginStudentSelectProgress.visibility = if (show) VISIBLE else GONE } override fun showContent(show: Boolean) { - binding.loginStudentSelectContent.isVisible = show + binding.loginStudentSelectContent.visibility = if (show) VISIBLE else GONE } override fun enableSignIn(enable: Boolean) { binding.loginStudentSelectSignIn.isEnabled = enable } - override fun openDiscordInvite() { - context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) - } - - override fun openEmail(supportInfo: LoginSupportInfo) { - LoginSupportDialog.newInstance(supportInfo).show(childFragmentManager, "support_dialog") + override fun showContact(show: Boolean) { + binding.loginStudentSelectContact.visibility = if (show) VISIBLE else GONE } override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() } + + override fun openDiscordInvite() { + context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) + } + + override fun openEmail(lastError: String) { + context?.openEmailClient( + chooserTitle = requireContext().getString(R.string.login_email_intent_title), + email = "wulkanowyinc@gmail.com", + subject = requireContext().getString(R.string.login_email_subject), + body = requireContext().getString( + R.string.login_email_text, appInfo.systemModel, + appInfo.systemVersion.toString(), + appInfo.versionName, + "Select users to log in", + lastError + ) + ) + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt deleted file mode 100644 index 1edc8e7b4..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectItem.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.studentselect - -import io.github.wulkanowy.data.pojos.RegisterStudent -import io.github.wulkanowy.data.pojos.RegisterSymbol -import io.github.wulkanowy.data.pojos.RegisterUnit - -sealed class LoginStudentSelectItem(val type: LoginStudentSelectItemType) { - - data class EmptySymbolsHeader( - val isExpanded: Boolean, - val onClick: () -> Unit, - ) : LoginStudentSelectItem(LoginStudentSelectItemType.EMPTY_SYMBOLS_HEADER) - - data class SymbolHeader( - val symbol: RegisterSymbol, - val humanReadableName: String?, - val isErrorExpanded: Boolean, - val onClick: (RegisterSymbol) -> Unit, - ) : LoginStudentSelectItem(LoginStudentSelectItemType.SYMBOL_HEADER) - - data class SchoolHeader( - val unit: RegisterUnit, - val isErrorExpanded: Boolean, - val onClick: (RegisterUnit) -> Unit, - ) : LoginStudentSelectItem(LoginStudentSelectItemType.SCHOOL_HEADER) - - data class Student( - val symbol: RegisterSymbol, - val unit: RegisterUnit, - val student: RegisterStudent, - val isEnabled: Boolean, - val isSelected: Boolean, - val onClick: (Student) -> Unit, - ) : LoginStudentSelectItem(LoginStudentSelectItemType.STUDENT) - - data class Help( - val onEnterSymbolClick: () -> Unit, - val onContactUsClick: () -> Unit, - val onDiscordClick: () -> Unit, - val isSymbolButtonVisible: Boolean, - ) : LoginStudentSelectItem(LoginStudentSelectItemType.HELP) -} - -enum class LoginStudentSelectItemType { - EMPTY_SYMBOLS_HEADER, - SYMBOL_HEADER, - SCHOOL_HEADER, - STUDENT, - HELP, -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt index 6cbdfbb85..3455b3cf1 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt @@ -1,59 +1,36 @@ package io.github.wulkanowy.ui.modules.login.studentselect import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.dataOrNull +import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.mappers.mapToStudentWithSemesters -import io.github.wulkanowy.data.pojos.RegisterStudent -import io.github.wulkanowy.data.pojos.RegisterSymbol -import io.github.wulkanowy.data.pojos.RegisterUnit -import io.github.wulkanowy.data.pojos.RegisterUser -import io.github.wulkanowy.data.repositories.SchoolsRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.resourceFlow -import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.modules.login.LoginData import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo import io.github.wulkanowy.utils.AnalyticsHelper -import io.github.wulkanowy.utils.AppInfo -import io.github.wulkanowy.utils.isCurrent +import io.github.wulkanowy.utils.ifNullOrBlank import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject class LoginStudentSelectPresenter @Inject constructor( studentRepository: StudentRepository, - private val schoolsRepository: SchoolsRepository, private val loginErrorHandler: LoginErrorHandler, private val syncManager: SyncManager, - private val analytics: AnalyticsHelper, - private val appInfo: AppInfo, + private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { private var lastError: Throwable? = null - private lateinit var registerUser: RegisterUser - private lateinit var loginData: LoginData + private val selectedStudents = mutableListOf() - private lateinit var students: List - private var isEmptySymbolsExpanded = false - private var expandedSymbolError: RegisterSymbol? = null - private var expandedSchoolError: RegisterUnit? = null - - private val selectedStudents = mutableListOf() - - fun onAttachView( - view: LoginStudentSelectView, - loginData: LoginData, - registerUser: RegisterUser, - ) { + fun onAttachView(view: LoginStudentSelectView, students: List) { super.onAttachView(view) with(view) { initView() + showContact(false) enableSignIn(false) loginErrorHandler.onStudentDuplicate = { showMessage(it) @@ -61,195 +38,50 @@ class LoginStudentSelectPresenter @Inject constructor( } } - this.loginData = loginData - this.registerUser = registerUser - loadData() + if (students.size == 1) registerStudents(students) + loadData(students) } - private fun loadData() { - resetSelectedState() - - resourceFlow { studentRepository.getSavedStudents(false) }.onEach { - students = it.dataOrNull.orEmpty() - when (it) { - is Resource.Loading -> Timber.d("Login student select students load started") - is Resource.Success -> { - getStudentsWithCurrentlyActiveSemesters() - selectedStudents.clear() - selectedStudents.addAll(getStudentsWithCurrentlyActiveSemesters()) - view?.enableSignIn(selectedStudents.isNotEmpty()) - refreshItems() - } - - is Resource.Error -> { - errorHandler.dispatch(it.error) - lastError = it.error - refreshItems() - } - } - }.launch() - } - - private fun getStudentsWithCurrentlyActiveSemesters(): List { - val students = registerUser.symbols.flatMap { symbol -> - symbol.schools.flatMap { unit -> - unit.students.map { - createStudentItem(it, symbol, unit, students) - } - } - } - return students - .filter { it.isEnabled } - .filter { student -> - student.student.semesters.any { semester -> - semester.isCurrent() - } - } - } - - private fun createItems(): List = buildList { - val notEmptySymbols = registerUser.symbols.filter { it.schools.isNotEmpty() } - val emptySymbols = registerUser.symbols.filter { it.schools.isEmpty() } - - if (emptySymbols.isNotEmpty() && notEmptySymbols.isNotEmpty() && emptySymbols.any { it.symbol == loginData.symbol }) { - add(createEmptySymbolItem(emptySymbols.first { it.symbol == loginData.symbol })) - } - - addAll(createNotEmptySymbolItems(notEmptySymbols, students)) - addAll(createEmptySymbolItems(emptySymbols, notEmptySymbols.isNotEmpty())) - - val helpItem = LoginStudentSelectItem.Help( - onEnterSymbolClick = ::onEnterSymbol, - onContactUsClick = ::onEmailClick, - onDiscordClick = ::onDiscordClick, - isSymbolButtonVisible = "login" !in loginData.baseUrl, - ) - add(helpItem) - } - - private fun createNotEmptySymbolItems( - notEmptySymbols: List, - students: List, - ) = buildList { - notEmptySymbols.forEach { registerSymbol -> - val symbolHeader = LoginStudentSelectItem.SymbolHeader( - symbol = registerSymbol, - humanReadableName = view?.symbols?.get(registerSymbol.symbol), - isErrorExpanded = expandedSymbolError == registerSymbol, - onClick = ::onSymbolItemClick, - ) - add(symbolHeader) - - registerSymbol.schools.forEach { registerUnit -> - val schoolHeader = LoginStudentSelectItem.SchoolHeader( - unit = registerUnit, - isErrorExpanded = expandedSchoolError == registerUnit, - onClick = ::onUnitItemClick, - ) - add(schoolHeader) - - registerUnit.students.forEach { - add(createStudentItem(it, registerSymbol, registerUnit, students)) - } - } - } - } - - private fun createStudentItem( - student: RegisterStudent, - symbol: RegisterSymbol, - school: RegisterUnit, - students: List, - ) = LoginStudentSelectItem.Student( - symbol = symbol, - unit = school, - student = student, - onClick = ::onItemSelected, - isEnabled = students.none { - it.student.email == registerUser.login - && it.student.symbol == symbol.symbol - && it.student.studentId == student.studentId - && it.student.schoolSymbol == school.schoolId - && it.student.classId == student.classId - }, - isSelected = selectedStudents - .filter { it.symbol.symbol == symbol.symbol } - .filter { it.unit.schoolId == school.schoolId } - .filter { it.student.studentId == student.studentId } - .filter { it.student.classId == student.classId } - .size == 1, - ) - - private fun createEmptySymbolItems( - emptySymbols: List, - isNotEmptySymbolsExist: Boolean, - ) = buildList { - val filteredEmptySymbols = emptySymbols.filter { - it.error !is InvalidSymbolException - }.ifEmpty { emptySymbols.takeIf { !isNotEmptySymbolsExist }.orEmpty() } - - if (filteredEmptySymbols.isNotEmpty() && isNotEmptySymbolsExist) { - val emptyHeader = LoginStudentSelectItem.EmptySymbolsHeader( - isExpanded = isEmptySymbolsExpanded, - onClick = ::onEmptySymbolsToggle, - ) - add(emptyHeader) - if (isEmptySymbolsExpanded) { - filteredEmptySymbols.forEach { - add(createEmptySymbolItem(it)) - } - } - } - - if (filteredEmptySymbols.isNotEmpty() && !isNotEmptySymbolsExist) { - filteredEmptySymbols.forEach { - add(createEmptySymbolItem(it)) - } - } - } - - private fun createEmptySymbolItem(registerSymbol: RegisterSymbol) = - LoginStudentSelectItem.SymbolHeader( - symbol = registerSymbol, - humanReadableName = view?.symbols?.get(registerSymbol.symbol), - isErrorExpanded = expandedSymbolError == registerSymbol, - onClick = ::onSymbolItemClick, - ) - fun onSignIn() { registerStudents(selectedStudents) } - private fun onEmptySymbolsToggle() { - isEmptySymbolsExpanded = !isEmptySymbolsExpanded - - refreshItems() - } - - private fun onItemSelected(item: LoginStudentSelectItem.Student) { - if (!item.isEnabled) return + fun onItemSelected(studentWithSemester: StudentWithSemesters, alreadySaved: Boolean) { + if (alreadySaved) return selectedStudents - .removeAll { - it.student.studentId == item.student.studentId && - it.student.classId == item.student.classId && - it.unit.schoolId == item.unit.schoolId && - it.symbol.symbol == item.symbol.symbol - } - .let { if (!it) selectedStudents.add(item) } + .removeAll { it == studentWithSemester } + .let { if (!it) selectedStudents.add(studentWithSemester) } view?.enableSignIn(selectedStudents.isNotEmpty()) - refreshItems() } - private fun onSymbolItemClick(symbol: RegisterSymbol) { - expandedSymbolError = if (symbol != expandedSymbolError) symbol else null - refreshItems() + private fun compareStudents(a: Student, b: Student): Boolean { + return a.email == b.email + && a.symbol == b.symbol + && a.studentId == b.studentId + && a.schoolSymbol == b.schoolSymbol + && a.classId == b.classId } - private fun onUnitItemClick(unit: RegisterUnit) { - expandedSchoolError = if (unit != expandedSchoolError) unit else null - refreshItems() + private fun loadData(studentsWithSemesters: List) { + resetSelectedState() + + resourceFlow { studentRepository.getSavedStudents(false) }.onEach { + when (it) { + is Resource.Loading -> Timber.d("Login student select students load started") + is Resource.Success -> view?.updateData(studentsWithSemesters.map { studentWithSemesters -> + studentWithSemesters to it.data.any { item -> + compareStudents(studentWithSemesters.student, item.student) + } + }) + is Resource.Error -> { + errorHandler.dispatch(it.error) + lastError = it.error + view?.updateData(studentsWithSemesters.map { student -> student to false }) + } + } + }.launch() } private fun resetSelectedState() { @@ -257,25 +89,8 @@ class LoginStudentSelectPresenter @Inject constructor( view?.enableSignIn(false) } - private fun refreshItems() { - view?.updateData(createItems()) - } - - private fun registerStudents(students: List) { - val filteredStudents = students.filterIsInstance() - val studentsWithSemesters = filteredStudents.map { item -> - item.student.mapToStudentWithSemesters( - user = registerUser, - symbol = item.symbol, - scrapperDomainSuffix = loginData.domainSuffix, - unit = item.unit, - colors = appInfo.defaultColorsForAvatar, - ) - } - resourceFlow { - studentRepository.saveStudents(studentsWithSemesters) - schoolsRepository.logSchoolLogin(loginData, studentsWithSemesters) - } + private fun registerStudents(studentsWithSemesters: List) { + resourceFlow { studentRepository.saveStudents(studentsWithSemesters) } .logResourceStatus("registration") .onEach { when (it) { @@ -283,17 +98,16 @@ class LoginStudentSelectPresenter @Inject constructor( showProgress(true) showContent(false) } - is Resource.Success -> { syncManager.startOneTimeSyncWorker(quiet = true) - view?.navigateToNext() + view?.openMainView() logRegisterEvent(studentsWithSemesters) } - is Resource.Error -> { view?.apply { showProgress(false) showContent(true) + showContact(true) } lastError = it.error loginErrorHandler.dispatch(it.error) @@ -303,23 +117,12 @@ class LoginStudentSelectPresenter @Inject constructor( }.launch("register") } - private fun onEnterSymbol() { - view?.navigateToSymbol(loginData) - } - - private fun onDiscordClick() { + fun onDiscordClick() { view?.openDiscordInvite() } - private fun onEmailClick() { - view?.openEmail( - LoginSupportInfo( - loginData = loginData, - registerUser = registerUser, - lastErrorMessage = lastError?.message, - enteredSymbol = loginData.symbol, - ) - ) + fun onEmailClick() { + view?.openEmail(lastError?.message.ifNullOrBlank { "empty" }) } private fun logRegisterEvent( diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt index b69700f17..f2acd76c5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt @@ -1,20 +1,15 @@ package io.github.wulkanowy.ui.modules.login.studentselect +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView -import io.github.wulkanowy.ui.modules.login.LoginData -import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo interface LoginStudentSelectView : BaseView { - val symbols: Map - fun initView() - fun updateData(data: List) + fun updateData(data: List>) - fun navigateToSymbol(loginData: LoginData) - - fun navigateToNext() + fun openMainView() fun showProgress(show: Boolean) @@ -22,7 +17,9 @@ interface LoginStudentSelectView : BaseView { fun enableSignIn(enable: Boolean) + fun showContact(show: Boolean) + fun openDiscordInvite() - fun openEmail(supportInfo: LoginSupportInfo) + fun openEmail(lastError: String) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/support/LoginSupportDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/support/LoginSupportDialog.kt deleted file mode 100644 index fcf7f51c9..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/support/LoginSupportDialog.kt +++ /dev/null @@ -1,149 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.support - -import android.os.Bundle -import android.util.Patterns -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.core.os.bundleOf -import androidx.core.widget.doOnTextChanged -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.databinding.DialogLoginSupportBinding -import io.github.wulkanowy.sdk.scrapper.login.AccountPermissionException -import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.AppInfo -import io.github.wulkanowy.utils.openEmailClient -import io.github.wulkanowy.utils.serializable -import javax.inject.Inject - -@AndroidEntryPoint -class LoginSupportDialog : BaseDialogFragment() { - - @Inject - lateinit var appInfo: AppInfo - - @Inject - lateinit var preferencesRepository: PreferencesRepository - - private lateinit var supportInfo: LoginSupportInfo - - companion object { - private const val ARGUMENT_KEY = "info" - - fun newInstance(info: LoginSupportInfo) = LoginSupportDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to info) - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_FRAME, R.style.WulkanowyTheme_NoActionBar) - supportInfo = requireArguments().serializable(ARGUMENT_KEY) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - val binding = DialogLoginSupportBinding.inflate(inflater) - .apply { binding = this } - binding.dialogLoginSupportToolbar.setNavigationOnClickListener { dismiss() } - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - with(binding) { - dialogLoginSupportSchoolInput.doOnTextChanged { _, _, _, _ -> - with(dialogLoginSupportSchoolLayout) { - isErrorEnabled = false - error = null - } - } - dialogLoginSupportSubmit.setOnClickListener { onSubmitClick() } - } - } - - private fun onSubmitClick() { - when { - binding.dialogLoginSupportSchoolInput.text.isNullOrBlank() -> { - with(binding.dialogLoginSupportSchoolLayout) { - isErrorEnabled = true - error = getString(R.string.error_field_required) - } - } - - Patterns.EMAIL_ADDRESS.matcher( - binding.dialogLoginSupportSchoolInput.text.toString() - ).matches() -> { - with(binding.dialogLoginSupportSchoolLayout) { - isErrorEnabled = true - error = getString(R.string.login_support_school_invalid) - } - } - - else -> { - openEmailClientWithFilledTemplate() - dismiss() - } - } - } - - private fun openEmailClientWithFilledTemplate() { - with(binding) { - context?.openEmailClient( - chooserTitle = requireContext().getString(R.string.login_email_intent_title), - email = "wulkanowyinc@gmail.com", - subject = requireContext().getString(R.string.login_email_subject), - body = requireContext().getString( - R.string.login_email_text, - "${appInfo.systemManufacturer} ${appInfo.systemModel}", - appInfo.systemVersion.toString(), - "${appInfo.versionName}-${appInfo.buildFlavor}", - supportInfo.loginData.baseUrl + "/" + supportInfo.loginData.symbol, - preferencesRepository.installationId, - getLastErrorFromStudentSelectScreen(), - dialogLoginSupportSchoolInput.text.takeIf { !it.isNullOrBlank() } - ?: return@with, - dialogLoginSupportAdditionalInput.text, - ) - ) - } - } - - private fun getLastErrorFromStudentSelectScreen(): String { - if (!supportInfo.lastErrorMessage.isNullOrBlank()) { - return supportInfo.lastErrorMessage!! - } - if (supportInfo.registerUser?.symbols.isNullOrEmpty()) { - return "" - } - - return "\n" + supportInfo.registerUser?.symbols?.filterNot { - (it.error is AccountPermissionException || it.error is InvalidSymbolException) && - it.symbol != supportInfo.enteredSymbol - }?.joinToString(";\n") { symbol -> - buildString { - append(" -") - append(symbol.symbol) - append("(${symbol.error?.message?.let { it.take(46) + "..." } ?: symbol.schools.size})") - if (symbol.schools.isNotEmpty()) { - append(": ") - } - append(symbol.schools.joinToString(", ") { unit -> - buildString { - append(unit.schoolShortName) - append("(${unit.error?.message?.let { it.take(46) + "..." } ?: unit.students.size})") - } - }) - } - } + "\nPozostałe: " + supportInfo.registerUser?.symbols?.filter { - it.error is AccountPermissionException || it.error is InvalidSymbolException - }?.joinToString(", ") { it.symbol } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/support/LoginSupportInfo.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/support/LoginSupportInfo.kt deleted file mode 100644 index 3f101dd63..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/support/LoginSupportInfo.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.wulkanowy.ui.modules.login.support - -import io.github.wulkanowy.data.pojos.RegisterUser -import io.github.wulkanowy.ui.modules.login.LoginData -import java.io.Serializable - -data class LoginSupportInfo( - val loginData: LoginData, - val registerUser: RegisterUser?, - val lastErrorMessage: String?, - val enteredSymbol: String?, -) : Serializable 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 23ebffe9d..58bdf6cef 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 @@ -12,18 +12,15 @@ import androidx.core.text.parseAsHtml import androidx.core.widget.doOnTextChanged import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.pojos.RegisterUser -import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.FragmentLoginSymbolBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginData -import io.github.wulkanowy.ui.modules.login.support.LoginSupportDialog -import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.openEmailClient import io.github.wulkanowy.utils.openInternetBrowser -import io.github.wulkanowy.utils.serializable import io.github.wulkanowy.utils.showSoftInput import javax.inject.Inject @@ -37,9 +34,6 @@ class LoginSymbolFragment : @Inject lateinit var appInfo: AppInfo - @Inject - lateinit var preferencesRepository: PreferencesRepository - companion object { private const val SAVED_LOGIN_DATA = "LOGIN_DATA" @@ -48,8 +42,6 @@ class LoginSymbolFragment : } } - override val symbolValue: String? get() = binding.loginSymbolName.text?.toString() - override val symbolNameError: CharSequence? get() = binding.loginSymbolNameLayout.error @@ -58,7 +50,7 @@ class LoginSymbolFragment : binding = FragmentLoginSymbolBinding.bind(view) presenter.onAttachView( view = this, - loginData = requireArguments().serializable(SAVED_LOGIN_DATA), + loginData = requireArguments().getSerializable(SAVED_LOGIN_DATA) as LoginData, ) } @@ -66,7 +58,7 @@ class LoginSymbolFragment : (requireActivity() as LoginActivity).showActionBar(true) with(binding) { - loginSymbolSignIn.setOnClickListener { presenter.attemptLogin() } + loginSymbolSignIn.setOnClickListener { presenter.attemptLogin(loginSymbolName.text.toString()) } loginSymbolFaq.setOnClickListener { presenter.onFaqClick() } loginSymbolContactEmail.setOnClickListener { presenter.onEmailClick() } @@ -99,28 +91,10 @@ class LoginSymbolFragment : } } - override fun setErrorSymbolInvalid() { - with(binding.loginSymbolNameLayout) { - requestFocus() - error = getString(R.string.login_invalid_symbol) - } - } - - override fun setErrorSymbolDefinitelyInvalid() { - with(binding.loginSymbolNameLayout) { - requestFocus() - error = getString(R.string.login_invalid_symbol_definitely) - } - } - override fun setErrorSymbolRequire() { - setErrorSymbol(getString(R.string.error_field_required)) - } - - override fun setErrorSymbol(message: String) { - with(binding.loginSymbolNameLayout) { + binding.loginSymbolNameLayout.apply { requestFocus() - error = message + error = getString(R.string.error_field_required) } } @@ -151,8 +125,8 @@ class LoginSymbolFragment : binding.loginSymbolContainer.visibility = if (show) VISIBLE else GONE } - override fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) { - (activity as? LoginActivity)?.navigateToStudentSelect(loginData, registerUser) + override fun navigateToStudentSelect(studentsWithSemesters: List) { + (activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters) } override fun onSaveInstanceState(outState: Bundle) { @@ -176,7 +150,19 @@ class LoginSymbolFragment : ) } - override fun openSupportDialog(supportInfo: LoginSupportInfo) { - LoginSupportDialog.newInstance(supportInfo).show(childFragmentManager, "support_dialog") + override fun openEmail(host: String, lastError: String) { + context?.openEmailClient( + chooserTitle = requireContext().getString(R.string.login_email_intent_title), + email = "wulkanowyinc@gmail.com", + subject = requireContext().getString(R.string.login_email_subject), + body = requireContext().getString( + R.string.login_email_text, + "${appInfo.systemManufacturer} ${appInfo.systemModel}", + appInfo.systemVersion.toString(), + appInfo.versionName, + "$host/${binding.loginSymbolName.text}", + lastError + ) + ) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt index 02bfde5d7..691cd4481 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt @@ -1,17 +1,12 @@ package io.github.wulkanowy.ui.modules.login.symbol import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.dataOrNull import io.github.wulkanowy.data.onResourceNotLoading -import io.github.wulkanowy.data.pojos.RegisterUser import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.resourceFlow -import io.github.wulkanowy.sdk.scrapper.getNormalizedSymbol -import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginData import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.ifNullOrBlank import kotlinx.coroutines.flow.onEach @@ -28,14 +23,9 @@ class LoginSymbolPresenter @Inject constructor( lateinit var loginData: LoginData - private var registerUser: RegisterUser? = null - fun onAttachView(view: LoginSymbolView, loginData: LoginData) { super.onAttachView(view) this.loginData = loginData - loginErrorHandler.onBadCredentials = { - view.setErrorSymbol(it.orEmpty()) - } with(view) { initView() showContact(false) @@ -49,39 +39,29 @@ class LoginSymbolPresenter @Inject constructor( view?.apply { if (symbolNameError != null) clearSymbolError() } } - fun attemptLogin() { - if (view?.symbolValue.isNullOrBlank()) { + fun attemptLogin(symbol: String) { + if (symbol.isBlank()) { view?.setErrorSymbolRequire() return } - if (isFormDefinitelyInvalid()) { - view?.setErrorSymbolDefinitelyInvalid() - return - } - loginData = loginData.copy( - symbol = view?.symbolValue?.getNormalizedSymbol(), - ) resourceFlow { - studentRepository.getUserSubjectsFromScrapper( + studentRepository.getStudentsScrapper( email = loginData.login, password = loginData.password, scrapperBaseUrl = loginData.baseUrl, - domainSuffix = loginData.domainSuffix, - symbol = loginData.symbol.orEmpty(), + symbol = symbol, ) - }.onEach { user -> - registerUser = user.dataOrNull - when (user) { + }.onEach { + when (it) { is Resource.Loading -> view?.run { Timber.i("Login with symbol started") hideSoftKeyboard() showProgress(true) showContent(false) } - is Resource.Success -> { - when (user.data.symbols.size) { + when (it.data.size) { 0 -> { Timber.i("Login with symbol result: Empty student list") view?.run { @@ -89,32 +69,20 @@ class LoginSymbolPresenter @Inject constructor( showContact(true) } } - else -> { - val enteredSymbolDetails = user.data.symbols - .firstOrNull() - ?.takeIf { it.symbol == loginData.symbol } - - if (enteredSymbolDetails?.error is InvalidSymbolException) { - view?.run { - setErrorSymbolInvalid() - showContact(true) - } - } else { - Timber.i("Login with symbol result: Success") - view?.navigateToStudentSelect(loginData, requireNotNull(user.data)) - } + Timber.i("Login with symbol result: Success") + view?.navigateToStudentSelect(requireNotNull(it.data)) } } analytics.logEvent( "registration_symbol", "success" to true, + "students" to it.data.size, "scrapperBaseUrl" to loginData.baseUrl, - "symbol" to view?.symbolValue, + "symbol" to symbol, "error" to "No error" ) } - is Resource.Error -> { Timber.i("Login with symbol result: An exception occurred") analytics.logEvent( @@ -122,11 +90,11 @@ class LoginSymbolPresenter @Inject constructor( "success" to false, "students" to -1, "scrapperBaseUrl" to loginData.baseUrl, - "symbol" to view?.symbolValue, - "error" to user.error.message.ifNullOrBlank { "No message" } + "symbol" to symbol, + "error" to it.error.message.ifNullOrBlank { "No message" } ) - loginErrorHandler.dispatch(user.error) - lastError = user.error + loginErrorHandler.dispatch(it.error) + lastError = it.error view?.showContact(true) } } @@ -138,25 +106,11 @@ class LoginSymbolPresenter @Inject constructor( }.launch("login") } - private fun isFormDefinitelyInvalid(): Boolean { - val definitelyInvalidSymbols = listOf("vulcan", "uonet", "wulkanowy", "standardowa") - val normalizedSymbol = view?.symbolValue.orEmpty().getNormalizedSymbol() - - return normalizedSymbol in definitelyInvalidSymbols - } - fun onFaqClick() { view?.openFaqPage() } fun onEmailClick() { - view?.openSupportDialog( - LoginSupportInfo( - loginData = loginData, - registerUser = registerUser, - lastErrorMessage = lastError?.message, - enteredSymbol = view?.symbolValue, - ) - ) + view?.openEmail(loginData.baseUrl, lastError?.message.ifNullOrBlank { "empty" }) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt index ace12f780..527895b77 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt @@ -1,14 +1,10 @@ package io.github.wulkanowy.ui.modules.login.symbol -import io.github.wulkanowy.data.pojos.RegisterUser +import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView -import io.github.wulkanowy.ui.modules.login.LoginData -import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo interface LoginSymbolView : BaseView { - val symbolValue: String? - val symbolNameError: CharSequence? fun initView() @@ -17,14 +13,8 @@ interface LoginSymbolView : BaseView { fun setErrorSymbolIncorrect() - fun setErrorSymbolInvalid() - - fun setErrorSymbolDefinitelyInvalid() - fun setErrorSymbolRequire() - fun setErrorSymbol(message: String) - fun clearSymbolError() fun clearAndFocusSymbol() @@ -37,11 +27,11 @@ interface LoginSymbolView : BaseView { fun showContent(show: Boolean) - fun navigateToStudentSelect(loginData: LoginData, registerUser: RegisterUser) + fun navigateToStudentSelect(studentsWithSemesters: List) fun showContact(show: Boolean) fun openFaqPage() - fun openSupportDialog(supportInfo: LoginSupportInfo) + fun openEmail(host: String, lastError: String) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt index a6c75c1d9..0a73fe15d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberFragment.kt @@ -18,7 +18,7 @@ import javax.inject.Inject @AndroidEntryPoint class LuckyNumberFragment : BaseFragment(R.layout.fragment_lucky_number), LuckyNumberView, - MainView.TitledView, MainView.MainChildView { + MainView.TitledView { @Inject lateinit var presenter: LuckyNumberPresenter @@ -86,14 +86,6 @@ class LuckyNumberFragment : (activity as? MainActivity)?.pushView(LuckyNumberHistoryFragment.newInstance()) } - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onViewReselected() - } - - override fun popView() { - (activity as? MainActivity)?.popView() - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt index 2039624b1..6f5c8e740 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt @@ -99,9 +99,4 @@ class LuckyNumberPresenter @Inject constructor( fun onDetailsClick() { view?.showErrorDetailsDialog(lastError) } - - fun onViewReselected() { - Timber.i("Luckynumber view is reselected") - view?.popView() - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt index 3d34ebc8f..0c05a1566 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberView.kt @@ -26,6 +26,4 @@ interface LuckyNumberView : BaseView { fun showContent(show: Boolean) fun openLuckyNumberHistory() - - fun popView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt index a78ce5dd2..53f06cacd 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 @@ -61,7 +61,7 @@ class LuckyNumberHistoryFragment : luckyNumberHistoryPreviousButton.setOnClickListener { presenter.onPreviousWeek() } luckyNumberHistoryNextButton.setOnClickListener { presenter.onNextWeek() } - luckyNumberHistoryNavContainer.elevation = requireContext().dpToPx(3f) + luckyNumberHistoryNavContainer.elevation = requireContext().dpToPx(8f) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt index a2d23e543..024beff85 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureActivity.kt @@ -1,12 +1,16 @@ package io.github.wulkanowy.ui.modules.luckynumberwidget -import android.appwidget.AppWidgetManager.* +import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID +import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS import android.content.Intent +import android.os.Build import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.ActivityWidgetConfigureBinding import io.github.wulkanowy.ui.base.BaseActivity @@ -37,6 +41,7 @@ class LuckyNumberWidgetConfigureActivity : setContentView( ActivityWidgetConfigureBinding.inflate(layoutInflater).apply { binding = this }.root ) + intent.extras.let { presenter.onAttachView(this, it?.getInt(EXTRA_APPWIDGET_ID)) } @@ -51,6 +56,22 @@ class LuckyNumberWidgetConfigureActivity : configureAdapter.onClickListener = presenter::onItemSelect } + override fun showThemeDialog() { + var items = arrayOf( + getString(R.string.widget_timetable_theme_light), + getString(R.string.widget_timetable_theme_dark) + ) + if (appInfo.systemVersion >= Build.VERSION_CODES.Q) items += (getString(R.string.widget_timetable_theme_system)) + + dialog = AlertDialog.Builder(this, R.style.WulkanowyTheme_WidgetAccountSwitcher) + .setTitle(R.string.widget_timetable_theme_title) + .setOnDismissListener { presenter.onDismissThemeView() } + .setSingleChoiceItems(items, -1) { _, which -> + presenter.onThemeSelect(which) + } + .show() + } + override fun updateData(data: List, selectedStudentId: Long) { with(configureAdapter) { selectedId = selectedStudentId diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt index 7e53dad06..cac648da8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt @@ -8,6 +8,7 @@ import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getThemeWidgetKey import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -31,9 +32,20 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor( fun onItemSelect(student: Student) { selectedStudent = student + view?.showThemeDialog() + } + + fun onThemeSelect(index: Int) { + appWidgetId?.let { + sharedPref.putLong(getThemeWidgetKey(it), index.toLong()) + } registerStudent(selectedStudent) } + fun onDismissThemeView() { + view?.finishView() + } + private fun loadData() { resourceFlow { studentRepository.getSavedStudents(false) }.onEach { when (it) { @@ -44,7 +56,10 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor( } ?: -1 when { it.data.isEmpty() -> view?.openLoginView() - it.data.size == 1 -> onItemSelect(it.data.single().student) + it.data.size == 1 -> { + selectedStudent = it.data.single().student + view?.showThemeDialog() + } else -> view?.updateData(it.data, selectedStudentId) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt index df13b993d..b4556f7ef 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigureView.kt @@ -7,6 +7,8 @@ interface LuckyNumberWidgetConfigureView : BaseView { fun initView() + fun showThemeDialog() + fun updateData(data: List, selectedStudentId: Long) fun updateLuckyNumberWidget(widgetId: Int) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt index bafb2d7e5..e03e3e90e 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 @@ -2,12 +2,14 @@ package io.github.wulkanowy.ui.modules.luckynumberwidget import android.app.PendingIntent import android.appwidget.AppWidgetManager +import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT +import android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH import android.appwidget.AppWidgetProvider import android.content.Context import android.content.res.Configuration import android.os.Bundle -import android.util.TypedValue.COMPLEX_UNIT_SP -import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE import android.widget.RemoteViews import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -15,6 +17,7 @@ import io.github.wulkanowy.data.Resource import io.github.wulkanowy.data.dataOrNull import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.data.repositories.LuckyNumberRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.toFirstResult @@ -38,12 +41,16 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { lateinit var sharedPref: SharedPrefProvider companion object { - private const val LUCKY_NUMBER_WIDGET_MAX_SIZE = 196 - private const val LUCKY_NUMBER_PENDING_INTENT_ID = 300 - private const val LUCKY_NUMBER_HISTORY_PENDING_INTENT_ID = 301 + 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" + + fun getHeightWidgetKey(appWidgetId: Int) = "lucky_number_widget_height_$appWidgetId" + + fun getWidthWidgetKey(appWidgetId: Int) = "lucky_number_widget_width_$appWidgetId" } override fun onUpdate( @@ -52,86 +59,107 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { 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 appIntent = PendingIntent.getActivity( - context, - LUCKY_NUMBER_PENDING_INTENT_ID, - SplashActivity.getStartIntent(context, Destination.LuckyNumber), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) + if (luckyNumber is Resource.Error) { + Timber.e("Error loading lucky number for widget", luckyNumber.error) + } - val historyIntent = PendingIntent.getActivity( - context, - LUCKY_NUMBER_HISTORY_PENDING_INTENT_ID, - SplashActivity.getStartIntent(context, Destination.LuckyNumberHistory), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) + val remoteView = + RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context)) + .apply { + setTextViewText( + R.id.luckyNumberWidgetNumber, + luckyNumber.dataOrNull?.luckyNumber?.toString() ?: "#" + ) + setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent) + } - appWidgetIds?.forEach { widgetId -> - val studentId = sharedPref.getLong(getStudentWidgetKey(widgetId), 0) - val luckyNumberResource = getLuckyNumber(studentId, widgetId) - val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber?.toString() - val remoteView = RemoteViews(context.packageName, R.layout.widget_luckynumber) - .apply { - setTextViewText(R.id.luckyNumberWidgetValue, luckyNumber ?: "-") - setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent) - setOnClickPendingIntent(R.id.luckyNumberWidgetHistoryButton, historyIntent) - } - - resizeWidget(context, appWidgetManager.getAppWidgetOptions(widgetId), remoteView) - appWidgetManager.updateAppWidget(widgetId, remoteView) - } - } - - override fun onAppWidgetOptionsChanged( - context: Context?, - appWidgetManager: AppWidgetManager?, - appWidgetId: Int, - newOptions: Bundle? - ) { - super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions) - - if (context == null || newOptions == null || appWidgetManager == null) { - return - } - - val remoteView = RemoteViews(context.packageName, R.layout.widget_luckynumber) - resizeWidget(context, newOptions, remoteView) - appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteView) - } - - private fun resizeWidget(context: Context, options: Bundle, remoteViews: RemoteViews) { - val (width, height) = options.getWidgetSize(context) - val size = minOf(width, height, LUCKY_NUMBER_WIDGET_MAX_SIZE).toFloat() - resizeWidgetContents(size, remoteViews) - Timber.v("LuckyNumberWidget resized: ${width}x${height} ($size)") - } - - private fun resizeWidgetContents(size: Float, remoteViews: RemoteViews) { - var historyButtonVisibility = View.VISIBLE - var luckyNumberTextSize = 72f - - if (size < 150) { - luckyNumberTextSize = 44f - historyButtonVisibility = View.GONE - } - if (size < 75) { - luckyNumberTextSize = 26f - } - - remoteViews.apply { - setTextViewTextSize(R.id.luckyNumberWidgetValue, COMPLEX_UNIT_SP, luckyNumberTextSize) - setViewVisibility(R.id.luckyNumberWidgetHistoryButton, historyButtonVisibility) + setStyles(remoteView, appWidgetId) + appWidgetManager.updateAppWidget(appWidgetId, remoteView) } } override fun onDeleted(context: Context?, appWidgetIds: IntArray?) { super.onDeleted(context, appWidgetIds) appWidgetIds?.forEach { appWidgetId -> - sharedPref.delete(getStudentWidgetKey(appWidgetId)) + with(sharedPref) { + delete(getHeightWidgetKey(appWidgetId)) + delete(getStudentWidgetKey(appWidgetId)) + delete(getThemeWidgetKey(appWidgetId)) + delete(getWidthWidgetKey(appWidgetId)) + } } } + override fun onAppWidgetOptionsChanged( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetId: Int, + newOptions: Bundle? + ) { + super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions) + + val remoteView = RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context)) + + setStyles(remoteView, appWidgetId, newOptions) + appWidgetManager.updateAppWidget(appWidgetId, remoteView) + } + + private fun setStyles(views: RemoteViews, appWidgetId: Int, options: Bundle? = null) { + val width = options?.getInt(OPTION_APPWIDGET_MIN_WIDTH) ?: sharedPref.getLong( + getWidthWidgetKey(appWidgetId), 74 + ).toInt() + val height = options?.getInt(OPTION_APPWIDGET_MAX_HEIGHT) ?: sharedPref.getLong( + getHeightWidgetKey(appWidgetId), 74 + ).toInt() + + with(sharedPref) { + putLong(getWidthWidgetKey(appWidgetId), width.toLong()) + putLong(getHeightWidgetKey(appWidgetId), height.toLong()) + } + + val rows = getCellsForSize(height) + val cols = getCellsForSize(width) + + Timber.d("New lucky number widget measurement: %dx%d", width, height) + Timber.d("Widget size: $cols x $rows") + + when { + 1 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = false) + 1 == cols && 1 < rows -> views.setVisibility(imageTop = true, imageLeft = false) + 1 < cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true) + 1 == cols && 1 == rows -> views.setVisibility(imageTop = true, imageLeft = false) + 2 == cols && 1 == rows -> views.setVisibility(imageTop = false, imageLeft = true) + else -> views.setVisibility(imageTop = false, imageLeft = false, title = true) + } + } + + private fun RemoteViews.setVisibility( + imageTop: Boolean, + imageLeft: Boolean, + title: Boolean = false + ) { + setViewVisibility(R.id.luckyNumberWidgetImageTop, if (imageTop) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetImageLeft, if (imageLeft) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetTitle, if (title) VISIBLE else GONE) + setViewVisibility(R.id.luckyNumberWidgetNumber, VISIBLE) + } + + private fun getCellsForSize(size: Int): Int { + var n = 2 + while (74 * n - 30 < size) ++n + return n - 1 + } + private fun getLuckyNumber(studentId: Long, appWidgetId: Int) = runBlocking { try { val students = studentRepository.getSavedStudents() @@ -153,24 +181,22 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() { Resource.Success(null) } } catch (e: Exception) { - Timber.e(e, "An error has occurred in lucky number provider") + if (e.cause !is NoCurrentStudentException) { + Timber.e(e, "An error has occurred in lucky number provider") + } Resource.Error(e) } } - private fun Bundle.getWidgetSize(context: Context): Pair { - val minWidth = getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH) - val maxWidth = getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH) - val minHeight = getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT) - val maxHeight = getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT) + private fun getCorrectLayoutId(appWidgetId: Int, context: Context): Int { + val savedTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0) + val isSystemDarkMode = + context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES - val orientation = context.resources.configuration.orientation - val isPortrait = orientation == Configuration.ORIENTATION_PORTRAIT - - return if (isPortrait) { - minWidth to maxHeight + return if (savedTheme == 1L || (savedTheme == 2L && isSystemDarkMode)) { + R.layout.widget_luckynumber_dark } else { - maxWidth to minHeight + R.layout.widget_luckynumber } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index ba0ef4050..5cd6fa103 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 @@ -2,18 +2,12 @@ package io.github.wulkanowy.ui.modules.main import android.content.Context import android.content.Intent -import android.os.Build +import android.os.Build.VERSION_CODES.P import android.os.Bundle import android.view.Menu import android.view.MenuItem -import android.view.ViewGroup.MarginLayoutParams -import androidx.activity.OnBackPressedCallback -import androidx.activity.addCallback import androidx.core.view.ViewCompat -import androidx.core.view.WindowCompat -import androidx.core.view.WindowInsetsCompat import androidx.core.view.isVisible -import androidx.core.view.updateLayoutParams import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.preference.Preference @@ -27,19 +21,11 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.ActivityMainBinding +import io.github.wulkanowy.databinding.DialogAdsConsentBinding import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.modules.Destination import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog -import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem -import io.github.wulkanowy.utils.AnalyticsHelper -import io.github.wulkanowy.utils.AppInfo -import io.github.wulkanowy.utils.InAppReviewHelper -import io.github.wulkanowy.utils.InAppUpdateHelper -import io.github.wulkanowy.utils.createNameInitialsDrawable -import io.github.wulkanowy.utils.dpToPx -import io.github.wulkanowy.utils.nickOrName -import io.github.wulkanowy.utils.safelyPopFragments -import io.github.wulkanowy.utils.setOnViewChangeListener +import io.github.wulkanowy.utils.* import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import timber.log.Timber @@ -56,7 +42,7 @@ class MainActivity : BaseActivity(), MainVie lateinit var analytics: AnalyticsHelper @Inject - lateinit var inAppUpdateHelper: InAppUpdateHelper + lateinit var updateHelper: UpdateHelper @Inject lateinit var inAppReviewHelper: InAppReviewHelper @@ -64,8 +50,6 @@ class MainActivity : BaseActivity(), MainVie @Inject lateinit var appInfo: AppInfo - private var onBackCallback: OnBackPressedCallback? = null - private var accountMenu: MenuItem? = null private val overlayProvider by lazy { ElevationOverlayProvider(this) } @@ -101,31 +85,28 @@ class MainActivity : BaseActivity(), MainVie super.onCreate(savedInstanceState) setContentView(ActivityMainBinding.inflate(layoutInflater).apply { binding = this }.root) setSupportActionBar(binding.mainToolbar) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - WindowCompat.setDecorFitsSystemWindows(window, false) - binding.mainAppBar.isLifted = true - } - initializeFragmentContainer() - this.savedInstanceState = savedInstanceState messageContainer = binding.mainMessageContainer - messageAnchor = binding.mainMessageContainer - inAppUpdateHelper.messageContainer = binding.mainFragmentContainer - onBackCallback = onBackPressedDispatcher.addCallback(this, enabled = false) { - presenter.onBackPressed() - } + updateHelper.messageContainer = binding.mainFragmentContainer val destination = intent.getStringExtra(EXTRA_START_DESTINATION) ?.takeIf { savedInstanceState == null } presenter.onAttachView(this, destination) - inAppUpdateHelper.checkAndInstallUpdates() + updateHelper.checkAndInstallUpdates(this) } override fun onResume() { super.onResume() - inAppUpdateHelper.onResume() + updateHelper.onResume(this) + } + + //https://developer.android.com/guide/playcore/in-app-updates#status_callback + @Deprecated("Deprecated in Java") + @Suppress("DEPRECATION") + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + updateHelper.onActivityResult(requestCode, resultCode) } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -136,20 +117,13 @@ class MainActivity : BaseActivity(), MainVie return true } - override fun initView( - startMenuIndex: Int, - rootAppMenuItems: List, - rootUpdatedDestinations: List - ) { + override fun initView(startMenuIndex: Int, rootDestinations: List) { initializeToolbar() - initializeBottomNavigation(startMenuIndex, rootAppMenuItems) - initializeNavController(startMenuIndex, rootUpdatedDestinations) + initializeBottomNavigation(startMenuIndex) + initializeNavController(startMenuIndex, rootDestinations) } - private fun initializeNavController( - startMenuIndex: Int, - rootUpdatedDestinations: List - ) { + private fun initializeNavController(startMenuIndex: Int, rootDestinations: List) { with(navController) { setOnViewChangeListener { destinationView -> presenter.onViewChange(destinationView) @@ -159,7 +133,7 @@ class MainActivity : BaseActivity(), MainVie ) } fragmentHideStrategy = HIDE - rootFragments = rootUpdatedDestinations.map { it.destinationFragment } + rootFragments = rootDestinations.map { it.destinationFragment } initialize(startMenuIndex, savedInstanceState) } @@ -175,16 +149,17 @@ class MainActivity : BaseActivity(), MainVie } } - private fun initializeBottomNavigation( - startMenuIndex: Int, - rootAppMenuItems: List - ) { + private fun initializeBottomNavigation(startMenuIndex: Int) { with(binding.mainBottomNav) { with(menu) { - rootAppMenuItems.forEachIndexed { index, item -> - add(Menu.NONE, index, Menu.NONE, item.title) - .setIcon(item.icon) - } + add(Menu.NONE, 0, Menu.NONE, R.string.dashboard_title) + .setIcon(R.drawable.ic_main_dashboard) + add(Menu.NONE, 1, Menu.NONE, R.string.grade_title) + .setIcon(R.drawable.ic_main_grade) + add(Menu.NONE, 2, Menu.NONE, R.string.attendance_title) + .setIcon(R.drawable.ic_main_attendance) + add(Menu.NONE, 3, Menu.NONE, R.string.timetable_title) + .setIcon(R.drawable.ic_main_timetable) add(Menu.NONE, 4, Menu.NONE, R.string.more_title) .setIcon(R.drawable.ic_main_more) } @@ -198,17 +173,6 @@ class MainActivity : BaseActivity(), MainVie } } - private fun initializeFragmentContainer() { - ViewCompat.setOnApplyWindowInsetsListener(binding.mainFragmentContainer) { view, insets -> - val bottomInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) - - view.updateLayoutParams { - bottomMargin = if (binding.mainBottomNav.isVisible) 0 else bottomInsets.bottom - } - WindowInsetsCompat.CONSUMED - } - } - override fun onPreferenceStartFragment( caller: PreferenceFragmentCompat, pref: Preference @@ -253,9 +217,20 @@ class MainActivity : BaseActivity(), MainVie showDialogFragment(AccountQuickDialog.newInstance(studentWithSemesters)) } + override fun showActionBarElevation(show: Boolean) { + ViewCompat.setElevation(binding.mainToolbar, if (show) dpToPx(4f) else 0f) + } + override fun showBottomNavigation(show: Boolean) { binding.mainBottomNav.isVisible = show - binding.mainFragmentContainer.requestApplyInsets() + + if (appInfo.systemVersion >= P) { + window.navigationBarColor = if (show) { + getThemeAttrColor(android.R.attr.navigationBarColor) + } else { + getThemeAttrColor(R.attr.colorSurface) + } + } } override fun openMoreDestination(destination: Destination) { @@ -291,7 +266,6 @@ class MainActivity : BaseActivity(), MainVie analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName) navController.pushFragment(fragment) - onBackCallback?.isEnabled = !isRootView } override fun popView(depth: Int) { @@ -299,7 +273,10 @@ class MainActivity : BaseActivity(), MainVie analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName) navController.safelyPopFragments(depth) - onBackCallback?.isEnabled = !isRootView + } + + override fun onBackPressed() { + presenter.onBackPressed { super.onBackPressed() } } override fun showStudentAvatar(student: Student) { @@ -323,6 +300,40 @@ class MainActivity : BaseActivity(), MainVie .show() } + override fun showPrivacyPolicyDialog() { + val dialogAdsConsentBinding = DialogAdsConsentBinding.inflate(layoutInflater) + + val dialog = MaterialAlertDialogBuilder(this) + .setTitle(R.string.pref_ads_consent_title) + .setMessage(R.string.pref_ads_consent_description) + .setView(dialogAdsConsentBinding.root) + .show() + + dialogAdsConsentBinding.adsConsentOver.setOnCheckedChangeListener { _, isChecked -> + dialogAdsConsentBinding.adsConsentPersonalised.isEnabled = isChecked + } + + dialogAdsConsentBinding.adsConsentPersonalised.setOnClickListener { + presenter.onPrivacyAgree(true) + dialog.dismiss() + } + + dialogAdsConsentBinding.adsConsentNonPersonalised.setOnClickListener { + presenter.onPrivacyAgree(false) + dialog.dismiss() + } + + dialogAdsConsentBinding.adsConsentPrivacy.setOnClickListener { presenter.onPrivacySelected() } + dialogAdsConsentBinding.adsConsentCancel.setOnClickListener { dialog.cancel() } + } + + override fun openPrivacyPolicy() { + openInternetBrowser( + "https://wulkanowy.github.io/polityka-prywatnosci.html", + ::showMessage + ) + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) navController.onSaveInstanceState(outState) 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 5469fcad3..9c32d8583 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 @@ -14,11 +14,15 @@ 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.utils.AdsHelper import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.AppInfo import kotlinx.coroutines.launch +import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import timber.log.Timber import java.time.Duration @@ -38,20 +42,20 @@ class MainPresenter @Inject constructor( private var studentsWitSemesters: List? = null - private val rootAppMenuItems = preferencesRepository.appMenuItemOrder - .sortedBy { it.order } - .take(4) - - private val rootDestinationTypeList = rootAppMenuItems.map { it.destinationType } - .plus(Destination.Type.MORE) + private val 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 -> 0 + this == null -> preferencesRepository.startMenuIndex destinationType in rootDestinationTypeList -> { rootDestinationTypeList.indexOf(destinationType) } - else -> 4 } @@ -65,7 +69,7 @@ class MainPresenter @Inject constructor( if (it == initDestination?.destinationType) initDestination else it.defaultDestination } - view.initView(startMenuIndex, rootAppMenuItems, destinations) + view.initView(startMenuIndex, destinations) if (initDestination != null && startMenuIndex == 4) { view.openMoreDestination(initDestination) } @@ -97,6 +101,7 @@ class MainPresenter @Inject constructor( fun onViewChange(destinationView: BaseView) { view?.apply { showBottomNavigation(shouldShowBottomNavigation(destinationView)) + showActionBarElevation(shouldShowActionBarElevation(destinationView)) currentViewTitle?.let { setViewTitle(it) } currentViewSubtitle?.let { setViewSubTitle(it.ifBlank { null }) } currentStackSize?.let { @@ -106,11 +111,17 @@ 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 } @@ -128,9 +139,12 @@ class MainPresenter @Inject constructor( return true } - fun onBackPressed() { + fun onBackPressed(default: () -> Unit) { Timber.i("Back pressed in main view") - view?.popView() + view?.run { + if (isRootView) default() + else popView() + } } fun onTabSelected(index: Int, wasSelected: Boolean): Boolean { @@ -149,8 +163,20 @@ class MainPresenter @Inject constructor( } fun onEnableAdsSelected() { - preferencesRepository.isAdsEnabled = true + view?.showPrivacyPolicyDialog() + } + + fun onPrivacyAgree(isPersonalizedAds: Boolean) { + preferencesRepository.isAgreeToProcessData = true + preferencesRepository.isPersonalizedAdsEnabled = isPersonalizedAds + adsHelper.initialize() + + preferencesRepository.isAdsEnabled = true + } + + fun onPrivacySelected() { + view?.openPrivacyPolicy() } private fun checkInAppReview() { @@ -178,8 +204,8 @@ class MainPresenter @Inject constructor( .getOrElse { return@launch } if (Instant.now().minus(Duration.ofDays(28)).isAfter(student.registrationDate)) { - preferencesRepository.isAppSupportShown = true view?.showAppSupport() + preferencesRepository.isAppSupportShown = true } } } 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 70a94fc81..3d018e3d6 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 @@ -4,7 +4,6 @@ import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.ui.base.BaseView import io.github.wulkanowy.ui.modules.Destination -import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem interface MainView : BaseView { @@ -16,11 +15,7 @@ interface MainView : BaseView { val currentStackSize: Int? - fun initView( - startMenuIndex: Int, - rootAppMenuItems: List, - rootUpdatedDestinations: List - ) + fun initView(startMenuIndex: Int, rootDestinations: List) fun switchMenuView(position: Int) @@ -28,6 +23,8 @@ interface MainView : BaseView { fun showAccountPicker(studentWithSemesters: List) + fun showActionBarElevation(show: Boolean) + fun showBottomNavigation(show: Boolean) fun notifyMenuViewReselected() @@ -46,6 +43,10 @@ interface MainView : BaseView { fun showAppSupport() + fun showPrivacyPolicyDialog() + + fun openPrivacyPolicy() + fun openMoreDestination(destination: Destination) interface MainChildView { 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 02bc13a16..4607793c9 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 @@ -11,13 +11,10 @@ import androidx.core.view.updateMargins import com.google.android.material.tabs.TabLayoutMediator import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED -import io.github.wulkanowy.data.enums.MessageFolder.SENT -import io.github.wulkanowy.data.enums.MessageFolder.TRASHED +import io.github.wulkanowy.data.enums.MessageFolder.* import io.github.wulkanowy.databinding.FragmentMessageBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity import io.github.wulkanowy.ui.modules.message.tab.MessageTabFragment @@ -27,7 +24,7 @@ import javax.inject.Inject @AndroidEntryPoint class MessageFragment : BaseFragment(R.layout.fragment_message), - MessageView, MainView.TitledView, MainView.MainChildView { + MessageView, MainView.TitledView { @Inject lateinit var presenter: MessagePresenter @@ -51,7 +48,6 @@ class MessageFragment : BaseFragment(R.layout.fragment_m override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentMessageBinding.bind(view) - messageContainer = binding.messageViewPager presenter.onAttachView(this) } @@ -98,10 +94,6 @@ class MessageFragment : BaseFragment(R.layout.fragment_m binding.messageProgress.visibility = if (show) VISIBLE else INVISIBLE } - override fun showMessage(messageId: Int) { - showMessage(getString(messageId)) - } - override fun showNewMessage(show: Boolean) { binding.openSendMessageButton.run { if (show) show() else hide() @@ -131,12 +123,8 @@ class MessageFragment : BaseFragment(R.layout.fragment_m presenter.onChildViewShowNewMessage(show) } - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onFragmentReselected() - } - - override fun onFragmentChanged() { - if (::presenter.isInitialized) presenter.onFragmentChanged() + fun onFragmentChanged() { + presenter.onFragmentChanged() } override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) { @@ -151,19 +139,10 @@ class MessageFragment : BaseFragment(R.layout.fragment_m } } - override fun notifyChildParentReselected(index: Int) { - (pagerAdapter.getFragmentInstance(index) as? MessageTabFragment) - ?.onParentReselected() - } - override fun openSendMessage() { context?.let { it.startActivity(SendMessageActivity.getStartIntent(it)) } } - override fun popView() { - (activity as? MainActivity)?.popView() - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt index 37a2d422a..68bdc4b7c 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 @@ -1,7 +1,5 @@ package io.github.wulkanowy.ui.modules.message -import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler @@ -11,8 +9,7 @@ import javax.inject.Inject class MessagePresenter @Inject constructor( errorHandler: ErrorHandler, - studentRepository: StudentRepository, - private val preferencesRepository: PreferencesRepository, + studentRepository: StudentRepository ) : BasePresenter(errorHandler, studentRepository) { override fun onAttachView(view: MessageView) { @@ -22,14 +19,6 @@ class MessagePresenter @Inject constructor( Timber.i("Message view was initialized") loadData() } - - showIncognitoModeReminderMessage() - } - - private fun showIncognitoModeReminderMessage() { - if (preferencesRepository.isIncognitoMode) { - view?.showMessage(R.string.message_incognito_mode_on) - } } fun onPageSelected(index: Int) { @@ -50,14 +39,6 @@ class MessagePresenter @Inject constructor( view?.notifyChildrenFinishActionMode() } - fun onFragmentReselected() { - Timber.i("Message view is reselected") - view?.run { - popView() - notifyChildParentReselected(currentPageIndex) - } - } - fun onChildViewLoaded() { view?.apply { showContent(true) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt index 7fdc6e181..e0cc5098c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.message -import androidx.annotation.StringRes import io.github.wulkanowy.ui.base.BaseView interface MessageView : BaseView { @@ -13,8 +12,6 @@ interface MessageView : BaseView { fun showProgress(show: Boolean) - fun showMessage(@StringRes messageId: Int) - fun showNewMessage(show: Boolean) fun showTabLayout(show: Boolean) @@ -23,9 +20,5 @@ interface MessageView : BaseView { fun notifyChildrenFinishActionMode() - fun notifyChildParentReselected(index: Int) - fun openSendMessage() - - fun popView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt deleted file mode 100644 index 59f6d288d..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt +++ /dev/null @@ -1,81 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.mailboxchooser - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.recyclerview.widget.DiffUtil.ItemCallback -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.data.db.entities.MailboxType -import io.github.wulkanowy.databinding.ItemMailboxChooserBinding -import javax.inject.Inject - -class MailboxChooserAdapter @Inject constructor() : - ListAdapter(Differ) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( - ItemMailboxChooserBinding.inflate( - LayoutInflater.from(parent.context), parent, false - ) - ) - - override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { - holder.bind(getItem(position)) - } - - class ItemViewHolder( - private val binding: ItemMailboxChooserBinding, - ) : RecyclerView.ViewHolder(binding.root) { - - fun bind(item: MailboxChooserItem) { - with(binding) { - mailboxItemName.text = item.mailbox?.getFirstLine() - ?: root.resources.getString(R.string.message_chip_all_mailboxes) - mailboxItemSchool.text = item.mailbox?.getSecondLine() - mailboxItemSchool.isVisible = !item.isAll - - root.setOnClickListener { item.onClickListener(item.mailbox) } - } - } - - private fun Mailbox.getFirstLine() = buildString { - if (studentName.isNotBlank() && studentName != userName) { - append(studentName) - append(" - ") - } - append(userName) - } - - private fun Mailbox.getSecondLine() = buildString { - append(schoolNameShort) - append(" - ") - append(getMailboxType(type)) - } - - private fun getMailboxType(type: MailboxType): String = when (type) { - MailboxType.STUDENT -> R.string.message_mailbox_type_student - MailboxType.PARENT -> R.string.message_mailbox_type_parent - MailboxType.GUARDIAN -> R.string.message_mailbox_type_guardian - MailboxType.EMPLOYEE -> R.string.message_mailbox_type_employee - MailboxType.UNKNOWN -> null - }.let { it?.let { it1 -> binding.root.resources.getString(it1) }.orEmpty() } - } - - private object Differ : ItemCallback() { - override fun areItemsTheSame( - oldItem: MailboxChooserItem, - newItem: MailboxChooserItem - ): Boolean { - return oldItem.mailbox?.globalKey == newItem.mailbox?.globalKey - } - - override fun areContentsTheSame( - oldItem: MailboxChooserItem, - newItem: MailboxChooserItem - ): Boolean { - return oldItem == newItem - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt deleted file mode 100644 index 8bd84f2bf..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt +++ /dev/null @@ -1,75 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.mailboxchooser - -import android.app.Dialog -import android.os.Bundle -import android.view.View -import androidx.core.os.bundleOf -import androidx.fragment.app.setFragmentResult -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.databinding.DialogMailboxChooserBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.parcelableArray -import javax.inject.Inject - -@AndroidEntryPoint -class MailboxChooserDialog : BaseDialogFragment(), MailboxChooserView { - - @Inject - lateinit var presenter: MailboxChooserPresenter - - @Inject - lateinit var mailboxAdapter: MailboxChooserAdapter - - companion object { - const val LISTENER_KEY = "mailbox_selected" - const val MAILBOX_KEY = "selected_mailbox" - const val REQUIRED_KEY = "is_mailbox_required" - - fun newInstance(mailboxes: List, isMailboxRequired: Boolean, folder: String) = - MailboxChooserDialog().apply { - arguments = bundleOf( - MAILBOX_KEY to mailboxes.toTypedArray(), - REQUIRED_KEY to isMailboxRequired, - LISTENER_KEY to folder, - ) - } - } - - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView( - DialogMailboxChooserBinding.inflate(layoutInflater).apply { binding = this }.root - ) - .create() - } - - - @Suppress("UNCHECKED_CAST") - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - presenter.onAttachView( - view = this, - requireMailbox = requireArguments().getBoolean(REQUIRED_KEY, false), - mailboxes = requireArguments().parcelableArray(MAILBOX_KEY).orEmpty().toList(), - ) - } - - override fun initView() { - binding.accountQuickDialogRecycler.adapter = mailboxAdapter - } - - override fun submitData(items: List) { - mailboxAdapter.submitList(items) - } - - override fun onMailboxSelected(item: Mailbox?) { - setFragmentResult( - requestKey = requireArguments().getString(LISTENER_KEY).orEmpty(), - result = bundleOf(MAILBOX_KEY to item), - ) - dismiss() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt deleted file mode 100644 index 6923cf085..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.mailboxchooser - -import io.github.wulkanowy.data.db.entities.Mailbox - -data class MailboxChooserItem( - val mailbox: Mailbox? = null, - val isAll: Boolean = false, - val onClickListener: (Mailbox?) -> Unit, -) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt deleted file mode 100644 index 5bd7c84ab..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.mailboxchooser - -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import timber.log.Timber -import javax.inject.Inject - -class MailboxChooserPresenter @Inject constructor( - errorHandler: ErrorHandler, - studentRepository: StudentRepository -) : BasePresenter(errorHandler, studentRepository) { - - fun onAttachView(view: MailboxChooserView, mailboxes: List, requireMailbox: Boolean) { - super.onAttachView(view) - - view.initView() - Timber.i("Mailbox chooser view was initialized") - view.submitData(getMailboxItems(mailboxes, requireMailbox)) - } - - private fun getMailboxItems( - mailboxes: List, - requireMailbox: Boolean, - ): List = buildList { - if (!requireMailbox) { - add(MailboxChooserItem(isAll = true, onClickListener = ::onMailboxSelect)) - } - addAll(mailboxes.map { - MailboxChooserItem(mailbox = it, isAll = false, onClickListener = ::onMailboxSelect) - }) - } - - fun onMailboxSelect(item: Mailbox?) { - view?.onMailboxSelected(item) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt deleted file mode 100644 index 2e20ee815..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.ui.modules.message.mailboxchooser - -import io.github.wulkanowy.data.db.entities.Mailbox -import io.github.wulkanowy.ui.base.BaseView - -interface MailboxChooserView : BaseView { - - fun initView() - - fun submitData(items: List) - - fun onMailboxSelected(item: Mailbox?) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt index d3c6b95c7..3c1c53d39 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.message.preview +import android.annotation.SuppressLint import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -73,20 +74,15 @@ class MessagePreviewAdapter @Inject constructor() : } } + @SuppressLint("SetTextI18n") private fun bindMessage(holder: MessageViewHolder, message: Message) { val context = holder.binding.root.context - val recipientCount = (message.unreadBy ?: 0) + (message.readBy ?: 0) - val isReceived = message.unreadBy == null - val readText = when { - recipientCount > 1 -> { - context.getString(R.string.message_read_by, message.readBy, recipientCount) - } - message.readBy == 1 || (isReceived && !message.unread) -> { - context.getString(R.string.message_read, context.getString(R.string.all_yes)) - } - else -> context.getString(R.string.message_read, context.getString(R.string.all_no)) + val readTextValue = when { + !message.unread -> R.string.all_yes + else -> R.string.all_no } + val readText = context.getString(R.string.message_read, context.getString(readTextValue)) with(holder.binding) { messagePreviewSubject.text = message.subject.ifBlank { 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 3ed685cd7..2a5523f4d 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 @@ -12,9 +12,7 @@ import android.view.View.VISIBLE import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient -import androidx.annotation.StringRes import androidx.core.content.getSystemService -import androidx.core.os.bundleOf import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -25,7 +23,6 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity -import io.github.wulkanowy.utils.serializable import io.github.wulkanowy.utils.shareText import javax.inject.Inject @@ -69,12 +66,13 @@ class MessagePreviewFragment : companion object { const val MESSAGE_ID_KEY = "message_id" - fun newInstance(message: Message) = MessagePreviewFragment().apply { - arguments = bundleOf(MESSAGE_ID_KEY to message) + fun newInstance(message: Message): MessagePreviewFragment { + return MessagePreviewFragment().apply { + arguments = Bundle().apply { putSerializable(MESSAGE_ID_KEY, message) } + } } } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -85,8 +83,8 @@ class MessagePreviewFragment : binding = FragmentMessagePreviewBinding.bind(view) messageContainer = binding.messagePreviewContainer presenter.onAttachView( - view = this, - message = (savedInstanceState ?: arguments)?.serializable(MESSAGE_ID_KEY), + this, + (savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as? Message ) } @@ -165,10 +163,6 @@ class MessagePreviewFragment : binding.messagePreviewErrorRetry.setOnClickListener { callback() } } - override fun showMessage(@StringRes messageId: Int) { - showMessage(getString(messageId)) - } - override fun openMessageReply(message: Message?) { context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message, true)) } } 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 cd7b72843..c011f41f3 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 @@ -2,13 +2,12 @@ package io.github.wulkanowy.ui.modules.message.preview import android.annotation.SuppressLint import androidx.core.text.parseAsHtml -import io.github.wulkanowy.R import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.MessageAttachment import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.data.repositories.MailboxRepository import io.github.wulkanowy.data.repositories.MessageRepository -import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler @@ -22,7 +21,7 @@ class MessagePreviewPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, - private val preferencesRepository: PreferencesRepository, + private val mailboxRepository: MailboxRepository, private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { @@ -57,11 +56,7 @@ class MessagePreviewPresenter @Inject constructor( private fun loadData(messageToLoad: Message) { flatResourceFlow { val student = studentRepository.getCurrentStudent() - messageRepository.getMessage( - student = student, - message = messageToLoad, - markAsRead = !preferencesRepository.isIncognitoMode, - ) + messageRepository.getMessage(student, messageToLoad, true) } .logResourceStatus("message ${messageToLoad.messageId} preview") .onResourceData { @@ -72,10 +67,6 @@ class MessagePreviewPresenter @Inject constructor( setMessageWithAttachment(it) showContent(true) initOptions() - - if (preferencesRepository.isIncognitoMode && it.message.unread) { - showMessage(R.string.message_incognito_description) - } } } else { view?.run { @@ -196,7 +187,7 @@ class MessagePreviewPresenter @Inject constructor( presenterScope.launch { runCatching { val student = studentRepository.getCurrentStudent(decryptPass = true) - val mailbox = messageRepository.getMailboxByStudent(student) + val mailbox = mailboxRepository.getMailbox(student) messageRepository.deleteMessage(student, mailbox, message!!) } .onFailure { 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 7f5f140b2..c5a947939 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,6 +1,5 @@ package io.github.wulkanowy.ui.modules.message.preview -import androidx.annotation.StringRes import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.ui.base.BaseView @@ -44,6 +43,4 @@ interface MessagePreviewView : BaseView { fun popView() fun printDocument(html: String, jobName: String) - - fun showMessage(@StringRes messageId: Int) } 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 0ba82f1a0..334e389e2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt @@ -1,10 +1,10 @@ package io.github.wulkanowy.ui.modules.message.send import android.annotation.SuppressLint +import android.app.AlertDialog import android.content.Context import android.content.Intent import android.graphics.Rect -import android.os.Build import android.os.Bundle import android.text.Spanned import android.view.Menu @@ -12,26 +12,18 @@ import android.view.MenuItem import android.view.TouchDelegate import android.view.View.GONE import android.view.View.VISIBLE -import android.view.ViewGroup import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.core.text.parseAsHtml import androidx.core.text.toHtml -import androidx.core.view.* import androidx.core.widget.doOnTextChanged -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.databinding.ActivitySendMessageBinding import io.github.wulkanowy.ui.base.BaseActivity -import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog -import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.LISTENER_KEY -import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.MAILBOX_KEY import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.nullableSerializable import io.github.wulkanowy.utils.showSoftInput import javax.inject.Inject @@ -102,30 +94,19 @@ class SendMessageActivity : BaseActivity= Build.VERSION_CODES.R) { - WindowCompat.setDecorFitsSystemWindows(window, false) - binding.sendAppBar.isLifted = true - } - initializeMessageContainer() - messageContainer = binding.sendMessageContainer formRecipientsData = binding.sendMessageTo.addedChipItems as List formSubjectValue = binding.sendMessageSubject.text.toString() formContentValue = binding.sendMessageMessageContent.text.toString().parseAsHtml().toString() - binding.sendMessageFrom.setOnClickListener { presenter.onOpenMailboxChooser() } presenter.onAttachView( view = this, - reason = intent.nullableSerializable(EXTRA_REASON), - message = intent.nullableSerializable(EXTRA_MESSAGE), - reply = intent.nullableSerializable(EXTRA_REPLY) + reason = intent.getSerializableExtra(EXTRA_REASON) as? String, + message = intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, + reply = intent.getSerializableExtra(EXTRA_REPLY) as? Boolean ) - supportFragmentManager.setFragmentResultListener(LISTENER_KEY, this) { _, bundle -> - presenter.onMailboxSelected(bundle.nullableSerializable(MAILBOX_KEY)) - } } @SuppressLint("ClickableViewAccessibility") @@ -140,22 +121,6 @@ class SendMessageActivity : BaseActivity - val navigationBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) - val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()) - - view.updateLayoutParams { - bottomMargin = if (imeInsets.bottom > navigationBarInsets.bottom) { - imeInsets.bottom - } else { - navigationBarInsets.bottom - } - } - WindowInsetsCompat.CONSUMED - } - } - private fun onMessageSubjectChange(text: CharSequence?) { formSubjectValue = text.toString() presenter.onMessageContentChange() @@ -240,14 +205,6 @@ class SendMessageActivity : BaseActivity) { - MailboxChooserDialog.newInstance( - mailboxes = mailboxes, - isMailboxRequired = true, - folder = LISTENER_KEY, - ).show(supportFragmentManager, "chooser") - } - override fun popView() { finish() } @@ -278,7 +235,7 @@ class SendMessageActivity : BaseActivity presenter.restoreMessageParts() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt index e776e9941..6c07ee6ad 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -1,15 +1,15 @@ package io.github.wulkanowy.ui.modules.message.send -import io.github.wulkanowy.data.* +import io.github.wulkanowy.data.Resource import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.MailboxType import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.logResourceStatus +import io.github.wulkanowy.data.onResourceNotLoading import io.github.wulkanowy.data.pojos.MessageDraft -import io.github.wulkanowy.data.repositories.MessageRepository -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.data.repositories.RecipientRepository -import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.* +import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AnalyticsHelper @@ -28,6 +28,7 @@ class SendMessagePresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, + private val mailboxRepository: MailboxRepository, private val recipientRepository: RecipientRepository, private val preferencesRepository: PreferencesRepository, private val analytics: AnalyticsHelper @@ -35,19 +36,10 @@ class SendMessagePresenter @Inject constructor( private val messageUpdateChannel = Channel() - private var message: Message? = null - private var isReplay: Boolean? = null - - private var mailboxes: List = emptyList() - private var selectedMailbox: Mailbox? = null - fun onAttachView(view: SendMessageView, reason: String?, message: Message?, reply: Boolean?) { super.onAttachView(view) view.initView() initializeSubjectStream() - this.message = message - this.isReplay = reply - Timber.i("Send message view was initialized") loadData(message, reply) with(view) { @@ -55,7 +47,7 @@ class SendMessagePresenter @Inject constructor( view.showMessageBackupDialog() } reason?.let { - setSubject("Usprawiedliwienie") + setSubject("Usprawiedliwenie") setContent(it) } message?.let { @@ -118,31 +110,16 @@ class SendMessagePresenter @Inject constructor( return false } - fun onOpenMailboxChooser() { - view?.showMailboxChooser(mailboxes) - } - - fun onMailboxSelected(mailbox: Mailbox?) { - selectedMailbox = mailbox - - loadData(message, isReplay) - } - private fun loadData(message: Message?, reply: Boolean?) { resourceFlow { val student = studentRepository.getCurrentStudent() - - if (selectedMailbox == null && mailboxes.isEmpty()) { - selectedMailbox = messageRepository.getMailboxByStudent(student) - mailboxes = messageRepository.getMailboxes(student, false).toFirstResult() - .dataOrNull.orEmpty() - } + val mailbox = mailboxRepository.getMailbox(student) Timber.i("Loading recipients started") val recipients = createChips( recipients = recipientRepository.getRecipients( student = student, - mailbox = selectedMailbox, + mailbox = mailbox, type = MailboxType.EMPLOYEE, ) ) @@ -153,7 +130,7 @@ class SendMessagePresenter @Inject constructor( message != null && reply == true -> recipientRepository.getMessageSender( student = student, message = message, - mailbox = selectedMailbox, + mailbox = mailbox, ) else -> emptyList() }.let { createChips(it) } @@ -162,42 +139,39 @@ class SendMessagePresenter @Inject constructor( messageRecipients.size ) - recipients to messageRecipients + Triple(mailbox, recipients, messageRecipients) } .logResourceStatus("load recipients") - .onResourceLoading { - view?.run { - showProgress(true) - showContent(false) - } - } - .onResourceNotLoading { - view?.run { showProgress(false) } - } - .onResourceError { - view?.showContent(true) - errorHandler.dispatch(it) - } - .onResourceSuccess { - it.let { (recipientChips, selectedRecipientChips) -> - view?.run { - setMailbox(getMailboxName(selectedMailbox)) - setRecipients(recipientChips) - if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients( - selectedRecipientChips - ) - showContent(true) + .onEach { + when (it) { + is Resource.Loading -> view?.run { + showProgress(true) + showContent(false) + } + is Resource.Success -> it.data.let { (mailbox, recipientChips, selectedRecipientChips) -> + view?.run { + setMailbox(getMailboxName(mailbox)) + setRecipients(recipientChips) + if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients( + selectedRecipientChips + ) + showContent(true) + } + } + is Resource.Error -> { + view?.showContent(true) + errorHandler.dispatch(it.error) } } - } - .launch() + }.onResourceNotLoading { + view?.run { showProgress(false) } + }.launch() } private fun sendMessage(subject: String, content: String, recipients: List) { - val mailbox = selectedMailbox ?: return - resourceFlow { val student = studentRepository.getCurrentStudent() + val mailbox = mailboxRepository.getMailbox(student) messageRepository.sendMessage( student = student, subject = subject, @@ -248,21 +222,18 @@ class SendMessagePresenter @Inject constructor( } } - private fun getMailboxName(mailbox: Mailbox?): String { - mailbox ?: return "" - - // username - accountType [\n student name - ] (school short name) + private fun getMailboxName(mailbox: Mailbox): String { return buildString { append(mailbox.userName) append(" - ") append(getMailboxType(mailbox.type)) - appendLine() if (mailbox.type == MailboxType.PARENT) { - append(mailbox.studentName) append(" - ") + append(mailbox.studentName) } + append(" - ") append("(${mailbox.schoolNameShort})") } } @@ -296,9 +267,9 @@ class SendMessagePresenter @Inject constructor( private fun saveDraftMessage() { messageRepository.draftMessage = MessageDraft( - recipients = view?.formRecipientsData!!, - subject = view?.formSubjectValue!!, - content = view?.formContentValue!!, + view?.formRecipientsData!!, + view?.formSubjectValue!!, + view?.formContentValue!! ) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt index e27a09d60..1057114b8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt @@ -61,5 +61,4 @@ interface SendMessageView : BaseView { fun getMessageBackupDialogStringWithRecipients(recipients: String): String fun clearDraft() - fun showMailboxChooser(mailboxes: List) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt index 9792c7085..55f03ef84 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt @@ -1,35 +1,28 @@ package io.github.wulkanowy.ui.modules.message.tab -import android.annotation.SuppressLint -import android.content.res.ColorStateList import android.graphics.Typeface import android.view.LayoutInflater import android.view.ViewGroup import android.widget.CompoundButton import androidx.core.view.isVisible -import androidx.core.widget.ImageViewCompat import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R import io.github.wulkanowy.databinding.ItemMessageBinding import io.github.wulkanowy.databinding.ItemMessageChipsBinding -import io.github.wulkanowy.utils.getCompatColor -import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.toFormattedString import javax.inject.Inject class MessageTabAdapter @Inject constructor() : RecyclerView.Adapter() { - lateinit var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit + var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit = { _, _ -> } - lateinit var onLongItemClickListener: (MessageTabDataItem.MessageItem) -> Unit + var onLongItemClickListener: (MessageTabDataItem.MessageItem) -> Unit = {} - lateinit var onHeaderClickListener: (CompoundButton, Boolean) -> Unit + var onHeaderClickListener: (CompoundButton, Boolean) -> Unit = { _, _ -> } - lateinit var onMailboxClickListener: () -> Unit - - lateinit var onChangesDetectedListener: () -> Unit + var onChangesDetectedListener = {} private var items = mutableListOf() @@ -53,12 +46,12 @@ class MessageTabAdapter @Inject constructor() : val inflater = LayoutInflater.from(parent.context) return when (MessageItemViewType.values()[viewType]) { - MessageItemViewType.FILTERS -> HeaderViewHolder( - ItemMessageChipsBinding.inflate(inflater, parent, false) - ) MessageItemViewType.MESSAGE -> ItemViewHolder( ItemMessageBinding.inflate(inflater, parent, false) ) + MessageItemViewType.FILTERS -> HeaderViewHolder( + ItemMessageChipsBinding.inflate(inflater, parent, false) + ) } } @@ -69,26 +62,10 @@ class MessageTabAdapter @Inject constructor() : } } - @SuppressLint("PrivateResource") private fun bindHeaderViewHolder(holder: HeaderViewHolder, position: Int) { val item = items[position] as MessageTabDataItem.FilterHeader - val context = holder.binding.root.context with(holder.binding) { - chipMailbox.text = - item.selectedMailbox ?: context.getString(R.string.message_chip_all_mailboxes) - chipMailbox.chipBackgroundColor = ColorStateList.valueOf( - if (item.selectedMailbox == null) { - context.getCompatColor(R.color.m3_elevated_chip_background_color) - } else context.getThemeAttrColor(R.attr.colorPrimary, 64) - ) - chipMailbox.setTextColor( - if (item.selectedMailbox == null) { - context.getThemeAttrColor(R.attr.colorOnSurfaceVariant) - } else context.getThemeAttrColor(R.attr.colorPrimary) - ) - chipMailbox.setOnClickListener { onMailboxClickListener() } - if (item.onlyUnread == null) { chipUnread.isVisible = false } else { @@ -97,7 +74,6 @@ class MessageTabAdapter @Inject constructor() : chipUnread.setOnCheckedChangeListener(onHeaderClickListener) } chipUnread.isEnabled = item.isEnabled - chipAttachments.isEnabled = item.isEnabled chipAttachments.isChecked = item.onlyWithAttachments chipAttachments.setOnCheckedChangeListener(onHeaderClickListener) @@ -109,35 +85,21 @@ class MessageTabAdapter @Inject constructor() : val message = item.message with(holder.binding) { - val normalFont = Typeface.create("sans-serif", Typeface.NORMAL) - val boldFont = Typeface.create("sans-serif-black", Typeface.NORMAL) - - val primaryColor = root.context.getThemeAttrColor(android.R.attr.textColorPrimary) - val secondaryColor = root.context.getThemeAttrColor(android.R.attr.textColorSecondary) - - val currentFont = if (message.unread) boldFont else normalFont - val currentTextColor = if (message.unread) primaryColor else secondaryColor + val style = if (message.unread) Typeface.BOLD else Typeface.NORMAL with(messageItemAuthor) { text = message.correspondents - setTextColor(currentTextColor) - typeface = currentFont + setTypeface(null, style) } - with(messageItemSubject) { + messageItemSubject.run { text = message.subject.ifBlank { context.getString(R.string.message_no_subject) } - setTextColor(currentTextColor) - typeface = currentFont + setTypeface(null, style) } - with(messageItemDate) { + messageItemDate.run { text = message.date.toFormattedString() - setTextColor(currentTextColor) - typeface = currentFont + setTypeface(null, style) } - with(messageItemAttachmentIcon) { - ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(currentTextColor)) - isVisible = message.hasAttachments - } - messageItemUnreadIndicator.isVisible = message.unread + messageItemAttachmentIcon.isVisible = message.hasAttachments root.setOnClickListener { holder.bindingAdapterPosition.let { @@ -149,7 +111,7 @@ class MessageTabAdapter @Inject constructor() : root.setOnLongClickListener { onLongItemClickListener(item) - true + return@setOnLongClickListener true } with(messageItemCheckbox) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt index c0bd4170e..634dfc0e7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt @@ -11,7 +11,6 @@ sealed class MessageTabDataItem(val viewType: MessageItemViewType) { ) : MessageTabDataItem(MessageItemViewType.MESSAGE) data class FilterHeader( - val selectedMailbox: String?, val onlyUnread: Boolean?, val onlyWithAttachments: Boolean, val isEnabled: Boolean diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt index 4364e8681..654b0e226 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -7,29 +7,23 @@ import android.view.MenuItem import android.view.View import android.view.View.* import android.widget.CompoundButton -import androidx.annotation.StringRes import androidx.appcompat.view.ActionMode import androidx.appcompat.widget.SearchView -import androidx.core.os.bundleOf import androidx.core.view.updatePadding -import androidx.fragment.app.setFragmentResultListener import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.databinding.FragmentMessageTabBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.message.MessageFragment -import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment import io.github.wulkanowy.ui.widgets.DividerItemDecoration import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.hideSoftInput -import io.github.wulkanowy.utils.nullableSerializable import javax.inject.Inject @AndroidEntryPoint @@ -46,8 +40,12 @@ class MessageTabFragment : BaseFragment(R.layout.frag const val MESSAGE_TAB_FOLDER_ID = "message_tab_folder_id" - fun newInstance(folder: MessageFolder) = MessageTabFragment().apply { - arguments = bundleOf(MESSAGE_TAB_FOLDER_ID to folder.name) + fun newInstance(folder: MessageFolder): MessageTabFragment { + return MessageTabFragment().apply { + arguments = Bundle().apply { + putString(MESSAGE_TAB_FOLDER_ID, folder.name) + } + } } } @@ -85,7 +83,6 @@ class MessageTabFragment : BaseFragment(R.layout.frag } } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -107,7 +104,6 @@ class MessageTabFragment : BaseFragment(R.layout.frag onItemClickListener = presenter::onMessageItemSelected onLongItemClickListener = presenter::onMessageItemLongSelected onHeaderClickListener = ::onChipChecked - onMailboxClickListener = presenter::onMailboxFilterSelected onChangesDetectedListener = ::resetListPosition } @@ -127,28 +123,15 @@ class MessageTabFragment : BaseFragment(R.layout.frag messageTabErrorRetry.setOnClickListener { presenter.onRetry() } messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() } } - - setFragmentResultListener(requireArguments().getString(MESSAGE_TAB_FOLDER_ID)!!) { _, bundle -> - presenter.onMailboxSelected( - mailbox = bundle.nullableSerializable(MailboxChooserDialog.MAILBOX_KEY), - ) - } } - @Deprecated("Deprecated in Java") - @Suppress("DEPRECATION") override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) inflater.inflate(R.menu.action_menu_message_tab, menu) - initializeSearchView(menu) - } - - private fun initializeSearchView(menu: Menu) { - val searchView = (menu.findItem(R.id.action_search).actionView as SearchView).apply { - queryHint = getString(R.string.all_search_hint) - maxWidth = Int.MAX_VALUE - } + val searchView = menu.findItem(R.id.action_search).actionView as SearchView + searchView.queryHint = getString(R.string.all_search_hint) + searchView.maxWidth = Int.MAX_VALUE searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String) = false override fun onQueryTextChange(query: String): Boolean { @@ -214,8 +197,8 @@ class MessageTabFragment : BaseFragment(R.layout.frag binding.messageTabSwipe.isRefreshing = show } - override fun showMessage(@StringRes messageId: Int) { - showMessage(getString(messageId)) + override fun showMessagesDeleted() { + showMessage(getString(R.string.message_messages_deleted)) } override fun notifyParentShowNewMessage(show: Boolean) { @@ -242,10 +225,6 @@ class MessageTabFragment : BaseFragment(R.layout.frag presenter.onParentFinishActionMode() } - fun onParentReselected() { - presenter.onParentReselected() - } - private fun onChipChecked(chip: CompoundButton, isChecked: Boolean) { when (chip.id) { R.id.chip_unread -> presenter.onUnreadFilterSelected(isChecked) @@ -267,16 +246,6 @@ class MessageTabFragment : BaseFragment(R.layout.frag ) } - override fun showMailboxChooser(mailboxes: List) { - (activity as? MainActivity)?.showDialogFragment( - MailboxChooserDialog.newInstance( - mailboxes = mailboxes, - isMailboxRequired = false, - folder = requireArguments().getString(MESSAGE_TAB_FOLDER_ID)!!, - ) - ) - } - override fun hideKeyboard() { activity?.hideSoftInput() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index 90f93b145..54711a689 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -1,10 +1,9 @@ package io.github.wulkanowy.ui.modules.message.tab -import io.github.wulkanowy.R import io.github.wulkanowy.data.* -import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.data.repositories.MailboxRepository import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter @@ -27,7 +26,8 @@ class MessageTabPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, - private val analytics: AnalyticsHelper, + private val mailboxRepository: MailboxRepository, + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { lateinit var folder: MessageFolder @@ -36,9 +36,6 @@ class MessageTabPresenter @Inject constructor( private var lastSearchQuery = "" - private var mailboxes: List = emptyList() - private var selectedMailbox: Mailbox? = null - private var messages = emptyList() private val searchChannel = Channel() @@ -84,14 +81,6 @@ class MessageTabPresenter @Inject constructor( view?.showActionMode(false) } - fun onParentReselected() { - view?.run { - if (!isViewEmpty) { - resetListPosition() - } - } - } - fun onDestroyActionMode() { isActionMode = false messagesToDelete.clear() @@ -133,10 +122,11 @@ class MessageTabPresenter @Inject constructor( runCatching { val student = studentRepository.getCurrentStudent(true) - messageRepository.deleteMessages(student, selectedMailbox, messageList) + val mailbox = mailboxRepository.getMailbox(student) + messageRepository.deleteMessages(student, mailbox, messageList) } .onFailure(errorHandler::dispatch) - .onSuccess { view?.showMessage(R.string.message_messages_deleted) } + .onSuccess { view?.showMessagesDeleted() } } } @@ -212,28 +202,13 @@ class MessageTabPresenter @Inject constructor( } } - fun onMailboxFilterSelected() { - view?.showMailboxChooser(mailboxes) - } - - fun onMailboxSelected(mailbox: Mailbox?) { - selectedMailbox = mailbox - loadData(false) - } - private fun loadData(forceRefresh: Boolean) { Timber.i("Loading $folder message data started") flatResourceFlow { val student = studentRepository.getCurrentStudent() - - if (selectedMailbox == null && mailboxes.isEmpty()) { - selectedMailbox = messageRepository.getMailboxByStudent(student) - mailboxes = messageRepository.getMailboxes(student, forceRefresh).toFirstResult() - .dataOrNull.orEmpty() - } - - messageRepository.getMessages(student, selectedMailbox, folder, forceRefresh) + val mailbox = mailboxRepository.getMailbox(student) + messageRepository.getMessages(student, mailbox, folder, forceRefresh) } .logResourceStatus("load $folder message") .onResourceData { @@ -352,16 +327,7 @@ class MessageTabPresenter @Inject constructor( MessageTabDataItem.FilterHeader( onlyUnread = onlyUnread.takeIf { folder != MessageFolder.SENT }, onlyWithAttachments = onlyWithAttachments, - isEnabled = !isActionMode, - selectedMailbox = selectedMailbox?.let { - buildString { - if (it.studentName.isNotBlank() && it.studentName != it.userName) { - append(it.studentName) - append(" - ") - } - append(it.userName) - } - }, + isEnabled = !isActionMode ) ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt index 247af4342..bfa43b209 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt @@ -1,7 +1,5 @@ package io.github.wulkanowy.ui.modules.message.tab -import androidx.annotation.StringRes -import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.ui.base.BaseView @@ -27,7 +25,7 @@ interface MessageTabView : BaseView { fun showEmpty(show: Boolean) - fun showMessage(@StringRes messageId: Int) + fun showMessagesDeleted() fun showErrorView(show: Boolean) @@ -48,6 +46,4 @@ interface MessageTabView : BaseView { fun showActionMode(show: Boolean) fun showRecyclerBottomPadding(show: Boolean) - - fun showMailboxChooser(mailboxes: List) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt index 1afe773cf..f8e367c57 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt @@ -22,7 +22,7 @@ import javax.inject.Inject @AndroidEntryPoint class MobileDeviceFragment : BaseFragment(R.layout.fragment_mobile_device), MobileDeviceView, - MainView.TitledView, MainView.MainChildView { + MainView.TitledView { @Inject lateinit var presenter: MobileDevicePresenter @@ -135,14 +135,6 @@ class MobileDeviceFragment : (activity as? MainActivity)?.showDialogFragment(MobileDeviceTokenDialog.newInstance()) } - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onFragmentReselected() - } - - override fun resetView() { - binding.mobileDevicesRecycler.smoothScrollToPosition(0) - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt index 56785dbf4..36a720e53 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 @@ -129,10 +129,4 @@ class MobileDevicePresenter @Inject constructor( .onResourceError(errorHandler::dispatch) .launch("unregister") } - - fun onFragmentReselected() { - if (view?.isViewEmpty == false) { - view?.resetView() - } - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt index 973cb1234..b94646a7b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt @@ -32,6 +32,4 @@ interface MobileDeviceView : BaseView { fun setErrorDetails(message: String) fun showTokenDialog() - - fun resetView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt index 2cc2a2aa7..eb420a6ae 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenDialog.kt @@ -1,17 +1,17 @@ package io.github.wulkanowy.ui.modules.mobiledevice.token -import android.app.Dialog import android.content.ClipData import android.content.ClipboardManager import android.graphics.BitmapFactory import android.os.Bundle import android.util.Base64 +import android.view.LayoutInflater import android.view.View import android.view.View.GONE import android.view.View.VISIBLE +import android.view.ViewGroup import android.widget.Toast import androidx.core.content.getSystemService -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.pojos.MobileDeviceToken @@ -31,14 +31,17 @@ class MobileDeviceTokenDialog : BaseDialogFragment(), fun newInstance() = MobileDeviceTokenDialog() } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView( - DialogMobileDeviceBinding.inflate(layoutInflater).apply { binding = this }.root - ) - .create() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogMobileDeviceBinding.inflate(inflater).apply { binding = this }.root + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) presenter.onAttachView(this) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt index 697eab330..70587b0cf 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreAdapter.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.more +import android.graphics.drawable.Drawable import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView @@ -8,9 +9,9 @@ import javax.inject.Inject class MoreAdapter @Inject constructor() : RecyclerView.Adapter() { - var items = emptyList() + var items = emptyList>() - var onClickListener: (moreItem: MoreItem) -> Unit = {} + var onClickListener: (name: String) -> Unit = {} override fun getItemCount() = items.size @@ -19,14 +20,13 @@ class MoreAdapter @Inject constructor() : RecyclerView.Adapter(R.layout.fragment_more), override val titleStringId: Int get() = R.string.more_title + override val messagesRes: Pair? + get() = context?.run { getString(R.string.message_title) to getCompatDrawable(R.drawable.ic_more_messages) } + + override val homeworkRes: Pair? + get() = context?.run { getString(R.string.homework_title) to getCompatDrawable(R.drawable.ic_more_homework) } + + override val noteRes: Pair? + get() = context?.run { getString(R.string.note_title) to getCompatDrawable(R.drawable.ic_more_note) } + + override val conferencesRes: Pair? + get() = context?.run { getString(R.string.conferences_title) to getCompatDrawable(R.drawable.ic_more_conferences) } + + override val schoolAnnouncementRes: Pair? + get() = context?.run { getString(R.string.school_announcement_title) to getCompatDrawable(R.drawable.ic_all_about) } + + override val schoolAndTeachersRes: Pair? + get() = context?.run { getString(R.string.schoolandteachers_title) to getCompatDrawable((R.drawable.ic_more_schoolandteachers)) } + + override val mobileDevicesRes: Pair? + get() = context?.run { getString(R.string.mobile_devices_title) to getCompatDrawable(R.drawable.ic_more_mobile_devices) } + + override val settingsRes: Pair? + get() = context?.run { getString(R.string.settings_title) to getCompatDrawable(R.drawable.ic_more_settings) } + + override val examRes: Pair? + get() = context?.run { getString(R.string.exam_title) to getCompatDrawable(R.drawable.ic_main_exam) } + + override 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) @@ -54,19 +94,55 @@ class MoreFragment : BaseFragment(R.layout.fragment_more), ?.onFragmentChanged() } - override fun updateData(data: List) { + override fun updateData(data: List>) { with(moreAdapter) { items = data notifyDataSetChanged() } } - override fun popView(depth: Int) { - (activity as? MainActivity)?.popView(depth) + override fun openMessagesView() { + (activity as? MainActivity)?.pushView(MessageFragment.newInstance()) } - override fun openView(destination: Destination) { - (activity as? MainActivity)?.pushView(destination.destinationFragment) + override fun openHomeworkView() { + (activity as? MainActivity)?.pushView(HomeworkFragment.newInstance()) + } + + override fun openNoteView() { + (activity as? MainActivity)?.pushView(NoteFragment.newInstance()) + } + + override fun openSchoolAnnouncementView() { + (activity as? MainActivity)?.pushView(SchoolAnnouncementFragment.newInstance()) + } + + override fun openConferencesView() { + (activity as? MainActivity)?.pushView(ConferenceFragment.newInstance()) + } + + override fun openSchoolAndTeachersView() { + (activity as? MainActivity)?.pushView(SchoolAndTeachersFragment.newInstance()) + } + + override fun openMobileDevicesView() { + (activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance()) + } + + override fun openSettingsView() { + (activity as? MainActivity)?.pushView(SettingsFragment.newInstance()) + } + + override fun openExamView() { + (activity as? MainActivity)?.pushView(ExamFragment.newInstance()) + } + + override fun openLuckyNumberView() { + (activity as? MainActivity)?.pushView(LuckyNumberFragment.newInstance()) + } + + override fun popView(depth: Int) { + (activity as? MainActivity)?.popView(depth) } override fun onDestroyView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt deleted file mode 100644 index d544f041c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreItem.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.github.wulkanowy.ui.modules.more - -import io.github.wulkanowy.ui.modules.Destination - -data class MoreItem( - - val icon: Int, - - val title: Int, - - val destination: Destination -) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt index 0ebaf4c7b..92551d6e9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt @@ -1,24 +1,16 @@ package io.github.wulkanowy.ui.modules.more -import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.ui.modules.Destination import timber.log.Timber import javax.inject.Inject class MorePresenter @Inject constructor( errorHandler: ErrorHandler, - studentRepository: StudentRepository, - preferencesRepository: PreferencesRepository + studentRepository: StudentRepository ) : BasePresenter(errorHandler, studentRepository) { - private val moreAppMenuItem = preferencesRepository.appMenuItemOrder - .sortedBy { it.order } - .drop(4) - override fun onAttachView(view: MoreView) { super.onAttachView(view) view.initView() @@ -26,10 +18,22 @@ class MorePresenter @Inject constructor( loadData() } - fun onItemSelected(moreItem: MoreItem) { - Timber.i("Select more item \"${moreItem.destination.destinationType}\"") - - view?.openView(moreItem.destination) + fun onItemSelected(title: String) { + Timber.i("Select more item \"${title}\"") + view?.run { + when (title) { + messagesRes?.first -> openMessagesView() + examRes?.first -> openExamView() + homeworkRes?.first -> openHomeworkView() + noteRes?.first -> openNoteView() + conferencesRes?.first -> openConferencesView() + schoolAnnouncementRes?.first -> openSchoolAnnouncementView() + schoolAndTeachersRes?.first -> openSchoolAndTeachersView() + mobileDevicesRes?.first -> openMobileDevicesView() + settingsRes?.first -> openSettingsView() + luckyNumberRes?.first -> openLuckyNumberView() + } + } } fun onViewReselected() { @@ -39,21 +43,19 @@ class MorePresenter @Inject constructor( private fun loadData() { Timber.i("Load items for more view") - val moreItems = moreAppMenuItem.map { - MoreItem( - icon = it.icon, - title = it.title, - destination = it.destinationType.defaultDestination - ) + view?.run { + updateData(listOfNotNull( + messagesRes, + examRes, + homeworkRes, + noteRes, + luckyNumberRes, + conferencesRes, + schoolAnnouncementRes, + schoolAndTeachersRes, + mobileDevicesRes, + settingsRes + )) } - .plus( - MoreItem( - icon = R.drawable.ic_more_settings, - title = R.string.settings_title, - destination = Destination.Settings - ) - ) - - view?.updateData(moreItems) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt index fbca97edc..cb895de28 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt @@ -1,15 +1,53 @@ package io.github.wulkanowy.ui.modules.more +import android.graphics.drawable.Drawable import io.github.wulkanowy.ui.base.BaseView -import io.github.wulkanowy.ui.modules.Destination interface MoreView : BaseView { + val messagesRes: Pair? + + val homeworkRes: Pair? + + val noteRes: Pair? + + val conferencesRes: Pair? + + val schoolAnnouncementRes: Pair? + + val schoolAndTeachersRes: Pair? + + val mobileDevicesRes: Pair? + + val settingsRes: Pair? + + val examRes: Pair? + + val luckyNumberRes: Pair? + fun initView() - fun updateData(data: List) + fun updateData(data: List>) + + fun openSettingsView() fun popView(depth: Int) - fun openView(destination: Destination) + fun openMessagesView() + + fun openHomeworkView() + + fun openNoteView() + + fun openSchoolAnnouncementView() + + fun openConferencesView() + + fun openSchoolAndTeachersView() + + fun openMobileDevicesView() + + fun openExamView() + + fun openLuckyNumberView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt index 0592e9243..5811456b6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt @@ -1,24 +1,23 @@ package io.github.wulkanowy.ui.modules.note import android.annotation.SuppressLint -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.core.content.ContextCompat -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.databinding.DialogNoteBinding import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory -import io.github.wulkanowy.ui.base.BaseDialogFragment import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.serializable +import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.toFormattedString -@AndroidEntryPoint -class NoteDialog : BaseDialogFragment() { +class NoteDialog : DialogFragment() { + + private var binding: DialogNoteBinding by lifecycleAwareVariable() private lateinit var note: Note @@ -26,21 +25,24 @@ class NoteDialog : BaseDialogFragment() { private const val ARGUMENT_KEY = "Item" - fun newInstance(note: Note) = NoteDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to note) + fun newInstance(exam: Note) = NoteDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - note = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + note = getSerializable(ARGUMENT_KEY) as Note + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogNoteBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogNoteBinding.inflate(inflater).apply { binding = this }.root @SuppressLint("SetTextI18n") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt index cb54b3840..dd6223448 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt @@ -18,7 +18,7 @@ import javax.inject.Inject @AndroidEntryPoint class NoteFragment : BaseFragment(R.layout.fragment_note), NoteView, - MainView.TitledView, MainView.MainChildView { + MainView.TitledView { @Inject lateinit var presenter: NotePresenter @@ -112,14 +112,6 @@ class NoteFragment : BaseFragment(R.layout.fragment_note), binding.noteSwipe.isRefreshing = show } - override fun onFragmentReselected() { - if (::presenter.isInitialized) presenter.onFragmentReselected() - } - - override fun resetView() { - binding.noteRecycler.smoothScrollToPosition(0) - } - override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt index 62ad347fa..440565e11 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 @@ -121,10 +121,4 @@ class NotePresenter @Inject constructor( } .launch("update_note") } - - fun onFragmentReselected() { - if (view?.isViewEmpty == false) { - view?.resetView() - } - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt index b813b3159..9fc0be94c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt @@ -30,6 +30,4 @@ interface NoteView : BaseView { fun showRefresh(show: Boolean) fun showNoteDialog(note: Note) - - fun resetView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notifications/NotificationsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notifications/NotificationsFragment.kt deleted file mode 100644 index 0763d4fa8..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/notifications/NotificationsFragment.kt +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.wulkanowy.ui.modules.notifications - -import android.os.Bundle -import android.view.View -import androidx.activity.result.contract.ActivityResultContracts.RequestPermission -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.databinding.FragmentNotificationsBinding -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.login.LoginActivity -import io.github.wulkanowy.utils.openNotificationSettings - -@AndroidEntryPoint -class NotificationsFragment : - BaseFragment(R.layout.fragment_notifications) { - - private val permission = "android.permission.POST_NOTIFICATIONS" - - private val requestPermissionLauncher = registerForActivityResult(RequestPermission()) { - if (it) { - navigateToFinish() - } else showSettingsDialog() - } - - companion object { - fun newInstance() = NotificationsFragment() - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding = FragmentNotificationsBinding.bind(view) - initView() - } - - private fun initView() { - with(binding) { - notificationsSkip.setOnClickListener { navigateToFinish() } - notificationsEnable.setOnClickListener { requestPermission() } - } - } - - private fun showSettingsDialog() { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.notifications_header_title) - .setMessage(R.string.notifications_header_description) - .setNegativeButton(R.string.notifications_skip) { dialog, _ -> - dialog.dismiss() - navigateToFinish() - } - .setPositiveButton(R.string.pref_notification_go_to_settings) { _, _ -> - requireActivity().openNotificationSettings() - } - .show() - } - - private fun requestPermission() { - requestPermissionLauncher.launch(permission) - } - - private fun navigateToFinish() { - (requireActivity() as LoginActivity).navigateToFinish() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt index fef06328d..e2af05c92 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 @@ -58,10 +58,7 @@ class TeacherPresenter @Inject constructor( .logResourceStatus("load teachers data") .onResourceData { view?.run { - updateData(it - .filter { item -> item.name.isNotBlank() } - .sortedBy { it.name } - ) + updateData(it.filter { item -> item.name.isNotBlank() }) showContent(it.isNotEmpty()) showEmpty(it.isEmpty()) showErrorView(false) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt index 46999599b..62f6251ec 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt @@ -2,10 +2,10 @@ package io.github.wulkanowy.ui.modules.schoolannouncement import android.view.LayoutInflater import android.view.ViewGroup +import androidx.core.text.parseAsHtml import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.data.db.entities.SchoolAnnouncement import io.github.wulkanowy.databinding.ItemSchoolAnnouncementBinding -import io.github.wulkanowy.utils.parseUonetHtml import io.github.wulkanowy.utils.toFormattedString import javax.inject.Inject @@ -28,7 +28,7 @@ class SchoolAnnouncementAdapter @Inject constructor() : with(holder.binding) { schoolAnnouncementItemDate.text = item.date.toFormattedString() schoolAnnouncementItemType.text = item.subject - schoolAnnouncementItemContent.text = item.content.parseUonetHtml() + schoolAnnouncementItemContent.text = item.content.parseAsHtml() root.setOnClickListener { onItemClickListener(item) } } 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 c1c584414..7dcd51cea 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 @@ -1,20 +1,19 @@ package io.github.wulkanowy.ui.modules.schoolannouncement -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import android.view.ViewGroup +import androidx.core.text.parseAsHtml +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.data.db.entities.SchoolAnnouncement import io.github.wulkanowy.databinding.DialogSchoolAnnouncementBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.parseUonetHtml -import io.github.wulkanowy.utils.serializable +import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.toFormattedString -@AndroidEntryPoint -class SchoolAnnouncementDialog : BaseDialogFragment() { +class SchoolAnnouncementDialog : DialogFragment() { + + private var binding: DialogSchoolAnnouncementBinding by lifecycleAwareVariable() private lateinit var announcement: SchoolAnnouncement @@ -22,24 +21,24 @@ class SchoolAnnouncementDialog : BaseDialogFragment)?.showMessage(text) } - override fun showExpiredCredentialsDialog() { - (activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog() - } - - override fun showDecryptionFailedDialog() { - (activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog() + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() } override fun showChangePasswordSnackbar(redirectUrl: String) { @@ -67,10 +62,6 @@ class AdvancedFragment : PreferenceFragmentCompat(), ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") - } - override fun onResume() { super.onResume() preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedPresenter.kt index 4bc24594a..d38f841fe 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedPresenter.kt @@ -18,8 +18,7 @@ class AdvancedPresenter @Inject constructor( Timber.i("Settings advanced view was initialized") } - fun onSharedPreferenceChanged(key: String?) { - key ?: return + fun onSharedPreferenceChanged(key: String) { Timber.i("Change settings $key") analytics.logEvent("setting_changed", "name" to key) } 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 20423eb91..1f6d5143b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt @@ -9,7 +9,6 @@ import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.ErrorDialog -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.AppInfo import javax.inject.Inject @@ -39,7 +38,7 @@ class AppearanceFragment : PreferenceFragmentCompat(), setPreferencesFromResource(R.xml.scheme_preferences_appearance, rootKey) } - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { presenter.onSharedPreferenceChanged(key) } @@ -63,12 +62,8 @@ class AppearanceFragment : PreferenceFragmentCompat(), (activity as? BaseActivity<*, *>)?.showMessage(text) } - override fun showExpiredCredentialsDialog() { - (activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog() - } - - override fun showDecryptionFailedDialog() { - (activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog() + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() } override fun showChangePasswordSnackbar(redirectUrl: String) { @@ -83,10 +78,6 @@ class AppearanceFragment : PreferenceFragmentCompat(), ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") - } - override fun onResume() { super.onResume() preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearancePresenter.kt index d3410ea84..14592a6cc 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearancePresenter.kt @@ -22,8 +22,7 @@ class AppearancePresenter @Inject constructor( Timber.i("Settings appearance view was initialized") } - fun onSharedPreferenceChanged(key: String?) { - key ?: return + fun onSharedPreferenceChanged(key: String) { Timber.i("Change settings $key") preferencesRepository.apply { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/AppMenuItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/AppMenuItem.kt deleted file mode 100644 index f522913ac..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/AppMenuItem.kt +++ /dev/null @@ -1,206 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import io.github.wulkanowy.R -import io.github.wulkanowy.ui.modules.Destination -import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient - -@Serializable -sealed class AppMenuItem { - - companion object { - val defaultAppMenuItemList = setOf( - DashboardAppMenuItem(), - GradeAppMenuItem(), - TimetableAppMenuItem(), - AttendanceAppMenuItem(), - ExamsAppMenuItem(), - HomeworkAppMenuItem(), - NoteAppMenuItem(), - LuckyNumberAppMenuItem(), - SchoolAnnouncementsAppMenuItem(), - SchoolAndTeachersAppMenuItem(), - MobileDevicesAppMenuItem(), - ConferenceAppMenuItem(), - MessageAppMenuItem() - ).sortedBy { it.order } - } - - // https://youtrack.jetbrains.com/issue/KT-38958 - abstract var order: Int - - abstract val icon: Int - - abstract val title: Int - - abstract val destinationType: Destination.Type - - @Serializable - data class DashboardAppMenuItem(override var order: Int = 0) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_main_dashboard - - @Transient - override val title = R.string.dashboard_title - - @Transient - override val destinationType = Destination.Type.DASHBOARD - } - - @Serializable - data class GradeAppMenuItem(override var order: Int = 1) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_main_grade - - @Transient - override val title = R.string.grade_title - - @Transient - override val destinationType = Destination.Type.GRADE - } - - @Serializable - data class AttendanceAppMenuItem(override var order: Int = 2) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_main_attendance - - @Transient - override val title = R.string.attendance_title - - @Transient - override val destinationType = Destination.Type.ATTENDANCE - } - - @Serializable - data class TimetableAppMenuItem(override var order: Int = 3) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_main_timetable - - @Transient - override val title = R.string.timetable_title - - @Transient - override val destinationType = Destination.Type.TIMETABLE - } - - @Serializable - data class MessageAppMenuItem(override var order: Int = 4) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_messages - - @Transient - override val title = R.string.message_title - - @Transient - override val destinationType = Destination.Type.MESSAGE - } - - @Serializable - data class ExamsAppMenuItem(override var order: Int = 5) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_main_exam - - @Transient - override val title = R.string.exam_title - - @Transient - override val destinationType = Destination.Type.EXAM - } - - @Serializable - data class HomeworkAppMenuItem(override var order: Int = 6) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_homework - - @Transient - override val title = R.string.homework_title - - @Transient - override val destinationType = Destination.Type.HOMEWORK - } - - @Serializable - data class NoteAppMenuItem(override var order: Int = 7) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_note - - @Transient - override val title = R.string.note_title - - @Transient - override val destinationType = Destination.Type.NOTE - } - - @Serializable - data class LuckyNumberAppMenuItem(override var order: Int = 8) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_lucky_number - - @Transient - override val title = R.string.lucky_number_title - - @Transient - override val destinationType = Destination.Type.LUCKY_NUMBER - } - - @Serializable - data class ConferenceAppMenuItem(override var order: Int = 9) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_conferences - - @Transient - override val title = R.string.conferences_title - - @Transient - override val destinationType = Destination.Type.CONFERENCE - } - - @Serializable - data class SchoolAnnouncementsAppMenuItem(override var order: Int = 10) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_all_about - - @Transient - override val title = R.string.school_announcement_title - - @Transient - override val destinationType = Destination.Type.SCHOOL_ANNOUNCEMENT - } - - @Serializable - data class SchoolAndTeachersAppMenuItem(override var order: Int = 11) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_schoolandteachers - - @Transient - override val title = R.string.schoolandteachers_title - - @Transient - override val destinationType = Destination.Type.SCHOOL_AND_TEACHERS - } - - @Serializable - data class MobileDevicesAppMenuItem(override var order: Int = 12) : AppMenuItem() { - - @Transient - override val icon = R.drawable.ic_more_mobile_devices - - @Transient - override val title = R.string.mobile_devices_title - - @Transient - override val destinationType = Destination.Type.MOBILE_DEVICE - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuItemMoveCallback.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuItemMoveCallback.kt deleted file mode 100644 index 49196fad2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuItemMoveCallback.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import androidx.recyclerview.widget.ItemTouchHelper -import androidx.recyclerview.widget.RecyclerView -import java.util.* - -class MenuItemMoveCallback( - private val menuOrderAdapter: MenuOrderAdapter, - private var onUserInteractionEndListener: (List) -> Unit = {} -) : ItemTouchHelper.Callback() { - - override fun isLongPressDragEnabled() = true - - override fun isItemViewSwipeEnabled() = false - - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { - //Not implemented - } - - override fun getMovementFlags( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder - ) = makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) - - override fun onMove( - recyclerView: RecyclerView, - viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder - ): Boolean { - val list = menuOrderAdapter.items.toMutableList() - - Collections.swap(list, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition) - - menuOrderAdapter.submitList(list) - return true - } - - override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { - super.clearView(recyclerView, viewHolder) - - onUserInteractionEndListener(menuOrderAdapter.items.toList()) - } -} - diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderAdapter.kt deleted file mode 100644 index 6bdd2fe0c..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderAdapter.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.RecyclerView -import io.github.wulkanowy.databinding.ItemMenuOrderBinding -import javax.inject.Inject - -class MenuOrderAdapter @Inject constructor() : - RecyclerView.Adapter() { - - val items = mutableListOf() - - fun submitList(newItems: List) { - val diffResult = DiffUtil.calculateDiff(DiffCallback(newItems, items.toMutableList())) - - with(items) { - clear() - addAll(newItems) - } - - diffResult.dispatchUpdatesTo(this) - } - - override fun getItemCount() = items.size - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( - ItemMenuOrderBinding.inflate(LayoutInflater.from(parent.context), parent, false) - ) - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val item = items[position].appMenuItem - - with(holder.binding) { - menuOrderItemTitle.setText(item.title) - menuOrderItemIcon.setImageResource(item.icon) - } - } - - class ViewHolder(val binding: ItemMenuOrderBinding) : RecyclerView.ViewHolder(binding.root) - - private class DiffCallback( - private val oldList: List, - private val newList: List - ) : DiffUtil.Callback() { - - override fun getNewListSize() = newList.size - - override fun getOldListSize() = oldList.size - - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = - oldList[oldItemPosition] == newList[newItemPosition] - - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = - oldList[oldItemPosition].appMenuItem.destinationType == newList[newItemPosition].appMenuItem.destinationType - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderDividerItemDecoration.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderDividerItemDecoration.kt deleted file mode 100644 index 21a7f5e80..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderDividerItemDecoration.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import android.content.Context -import android.graphics.Canvas -import android.graphics.Rect -import android.graphics.drawable.ShapeDrawable -import android.view.View -import androidx.core.graphics.drawable.DrawableCompat -import androidx.core.view.forEach -import androidx.recyclerview.widget.DividerItemDecoration -import androidx.recyclerview.widget.RecyclerView -import io.github.wulkanowy.R -import io.github.wulkanowy.utils.getThemeAttrColor - -class MenuOrderDividerItemDecoration(private val context: Context) : - DividerItemDecoration(context, VERTICAL) { - - private val dividerDrawable = ShapeDrawable() - .apply { - DrawableCompat.setTint(this, context.getThemeAttrColor(R.attr.colorDivider)) - } - - override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) { - canvas.save() - val dividerLeft = parent.paddingLeft - val dividerRight = parent.width - parent.paddingRight - - parent.forEach { - if (parent.getChildAdapterPosition(it) == 3) { - val params = it.layoutParams as RecyclerView.LayoutParams - val dividerTop = it.bottom + params.bottomMargin - val dividerBottom = dividerTop + dividerDrawable.intrinsicHeight - - dividerDrawable.setBounds(dividerLeft, dividerTop, dividerRight, dividerBottom + 30) - dividerDrawable.draw(canvas) - } - } - - canvas.restore() - } - - override fun getItemOffsets( - outRect: Rect, view: View, parent: RecyclerView, - state: RecyclerView.State - ) { - if (parent.getChildAdapterPosition(view) == 3) { - outRect.bottom = dividerDrawable.intrinsicHeight + 30 - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderFragment.kt deleted file mode 100644 index e08fc5dd3..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderFragment.kt +++ /dev/null @@ -1,101 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import androidx.activity.addCallback -import androidx.core.view.MenuProvider -import androidx.recyclerview.widget.ItemTouchHelper -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.SimpleItemAnimator -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.databinding.FragmentMenuOrderBinding -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainActivity -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.ui.widgets.DividerItemDecoration -import javax.inject.Inject - - -@AndroidEntryPoint -class MenuOrderFragment : BaseFragment(R.layout.fragment_menu_order), - MenuOrderView, MainView.TitledView { - - @Inject - lateinit var presenter: MenuOrderPresenter - - @Inject - lateinit var menuOrderAdapter: MenuOrderAdapter - - override val titleStringId = R.string.menu_order_title - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding = FragmentMenuOrderBinding.bind(view) - presenter.onAttachView(this) - } - - override fun initView() { - val itemTouchHelper = ItemTouchHelper( - MenuItemMoveCallback(menuOrderAdapter, presenter::onDragAndDropEnd) - ) - - itemTouchHelper.attachToRecyclerView(binding.menuOrderRecycler) - - with(binding.menuOrderRecycler) { - layoutManager = LinearLayoutManager(context) - adapter = menuOrderAdapter - addItemDecoration(MenuOrderDividerItemDecoration(context)) - addItemDecoration(DividerItemDecoration(context)) - (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false - } - - requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { - presenter.onBackSelected() - } - - initializeToolbar() - } - - private fun initializeToolbar() { - requireActivity().addMenuProvider(object : MenuProvider { - override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { - } - - override fun onMenuItemSelected(menuItem: MenuItem): Boolean { - if (menuItem.itemId == android.R.id.home) { - presenter.onBackSelected() - return true - } - return false - } - - }, viewLifecycleOwner) - } - - override fun updateData(data: List) { - menuOrderAdapter.submitList(data) - } - - override fun restartApp() { - startActivity(MainActivity.getStartIntent(requireContext())) - requireActivity().finishAffinity() - } - - override fun popView() { - (activity as? MainActivity?)?.popView() - } - - override fun showRestartConfirmationDialog() { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.menu_order_confirm_title) - .setMessage(R.string.menu_order_confirm_content) - .setPositiveButton(R.string.menu_order_confirm_restart) { _, _ -> presenter.onConfirmRestart() } - .setNegativeButton(R.string.all_cancel) { _, _ -> presenter.onCancelRestart() } - .show() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderItem.kt deleted file mode 100644 index b82bc1bd2..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderItem.kt +++ /dev/null @@ -1,6 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -data class MenuOrderItem( - val appMenuItem: AppMenuItem, - val order: Int -) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderPresenter.kt deleted file mode 100644 index 1c90cc5e6..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderPresenter.kt +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import timber.log.Timber -import javax.inject.Inject - -class MenuOrderPresenter @Inject constructor( - studentRepository: StudentRepository, - errorHandler: ErrorHandler, - private val preferencesRepository: PreferencesRepository -) : BasePresenter(errorHandler, studentRepository) { - - private var updatedMenuOrderItems = emptyList() - - override fun onAttachView(view: MenuOrderView) { - super.onAttachView(view) - view.initView() - Timber.i("Menu order view was initialized") - loadData() - } - - private fun loadData() { - val savedMenuItemList = (preferencesRepository.appMenuItemOrder) - .sortedBy { it.order } - .map { MenuOrderItem(it, it.order) } - - view?.updateData(savedMenuItemList) - } - - fun onDragAndDropEnd(list: List) { - val updatedList = list.mapIndexed { index, menuOrderItem -> - menuOrderItem.copy(order = index) - } - - updatedMenuOrderItems = updatedList - view?.updateData(updatedList) - } - - fun onBackSelected() { - if (updatedMenuOrderItems.isNotEmpty()) { - view?.showRestartConfirmationDialog() - } else { - view?.popView() - } - } - - fun onConfirmRestart() { - updatedMenuOrderItems.forEach { - it.appMenuItem.apply { - order = it.order - } - } - - preferencesRepository.appMenuItemOrder = updatedMenuOrderItems.map { it.appMenuItem } - view?.restartApp() - } - - fun onCancelRestart() { - view?.popView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderView.kt deleted file mode 100644 index 264e68ccb..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/menuorder/MenuOrderView.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.ui.modules.settings.appearance.menuorder - -import io.github.wulkanowy.ui.base.BaseView - -interface MenuOrderView : BaseView { - - fun initView() - - fun updateData(data: List) - - fun restartApp() - - fun showRestartConfirmationDialog() - - fun popView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt index 2ae983c26..364ad2137 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt @@ -1,31 +1,32 @@ package io.github.wulkanowy.ui.modules.settings.notifications +import android.annotation.SuppressLint import android.content.Intent import android.content.SharedPreferences -import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build import android.os.Bundle +import android.provider.Settings import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AlertDialog import androidx.core.app.NotificationManagerCompat -import androidx.core.content.ContextCompat import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreferenceCompat import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.thelittlefireman.appkillermanager.AppKillerManager import com.thelittlefireman.appkillermanager.exceptions.NoActionFoundException import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.ErrorDialog -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.openInternetBrowser -import io.github.wulkanowy.utils.openNotificationSettings +import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint @@ -41,14 +42,7 @@ class NotificationsFragment : PreferenceFragmentCompat(), override val titleStringId get() = R.string.pref_settings_notifications_title - private val notificationsPermission = "android.permission.POST_NOTIFICATIONS" - override val isNotificationPermissionGranted: Boolean - get() = ContextCompat.checkSelfPermission( - requireContext(), notificationsPermission - ) == PackageManager.PERMISSION_GRANTED - - override val isNotificationPiggybackPermissionGranted: Boolean get() { val packageNameList = NotificationManagerCompat.getEnabledListenerPackages(requireContext()) @@ -57,13 +51,6 @@ class NotificationsFragment : PreferenceFragmentCompat(), return appPackageName in packageNameList } - private val requestPermissionLauncher = - registerForActivityResult(ActivityResultContracts.RequestPermission()) { - if (it) { - presenter.onNotificationsPermissionResult() - } else openNotificationsPermissionDialog() - } - private val notificationSettingsPiggybackContract = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { presenter.onNotificationPiggybackPermissionResult() @@ -114,7 +101,7 @@ class NotificationsFragment : PreferenceFragmentCompat(), setPreferencesFromResource(R.xml.scheme_preferences_notifications, rootKey) } - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { presenter.onSharedPreferenceChanged(key) } @@ -133,12 +120,8 @@ class NotificationsFragment : PreferenceFragmentCompat(), (activity as? BaseActivity<*, *>)?.showMessage(text) } - override fun showExpiredCredentialsDialog() { - (activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog() - } - - override fun showDecryptionFailedDialog() { - (activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog() + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() } override fun showChangePasswordSnackbar(redirectUrl: String) { @@ -153,12 +136,8 @@ class NotificationsFragment : PreferenceFragmentCompat(), ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") - } - override fun showFixSyncDialog() { - MaterialAlertDialogBuilder(requireContext()) + AlertDialog.Builder(requireContext()) .setTitle(R.string.pref_notify_fix_sync_issues) .setMessage(R.string.pref_notify_fix_sync_issues_message) .setNegativeButton(android.R.string.cancel) { _, _ -> } @@ -177,30 +156,26 @@ class NotificationsFragment : PreferenceFragmentCompat(), .show() } + @SuppressLint("InlinedApi") override fun openSystemSettings() { - requireActivity().openNotificationSettings() - } - - override fun requestNotificationPermissions() { - requestPermissionLauncher.launch(notificationsPermission) - } - - override fun openNotificationsPermissionDialog() { - MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.notifications_header_title) - .setMessage(R.string.notifications_header_description) - .setPositiveButton(R.string.pref_notification_go_to_settings) { _, _ -> - requireActivity().openNotificationSettings() + val intent = if (appInfo.systemVersion >= Build.VERSION_CODES.O) { + Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { + putExtra("android.provider.extra.APP_PACKAGE", requireActivity().packageName) } - .setNegativeButton(android.R.string.cancel) { _, _ -> - setNotificationPreferencesChecked(false) + } else { + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.fromParts("package", requireActivity().packageName, null) } - .setOnDismissListener { setNotificationPreferencesChecked(false) } - .show() + } + try { + requireActivity().startActivity(intent) + } catch (e: Exception) { + Timber.e(e) + } } - override fun openNotificationPiggyBackPermissionDialog() { - MaterialAlertDialogBuilder(requireContext()) + 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)) { _, _ -> @@ -214,7 +189,7 @@ class NotificationsFragment : PreferenceFragmentCompat(), } override fun openNotificationExactAlarmSettings() { - MaterialAlertDialogBuilder(requireContext()) + 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)) { _, _ -> @@ -227,11 +202,6 @@ class NotificationsFragment : PreferenceFragmentCompat(), .show() } - override fun setNotificationPreferencesChecked(isChecked: Boolean) { - findPreference(getString(R.string.pref_key_notifications_enable))?.isChecked = - isChecked - } - override fun setNotificationPiggybackPreferenceChecked(isChecked: Boolean) { findPreference(getString(R.string.pref_key_notifications_piggyback))?.isChecked = isChecked 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 b2938cf65..4cbdac945 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsPresenter.kt @@ -26,20 +26,18 @@ class NotificationsPresenter @Inject constructor( with(view) { enableNotification( - notificationKey = preferencesRepository.notificationsEnableKey, - enable = preferencesRepository.isServiceEnabled + preferencesRepository.notificationsEnableKey, + preferencesRepository.isServiceEnabled ) initView(appInfo.isDebug) } - checkNotificationsPermissionState() checkNotificationPiggybackState() Timber.i("Settings notifications view was initialized") } - fun onSharedPreferenceChanged(key: String?) { - key ?: return + fun onSharedPreferenceChanged(key: String) { Timber.i("Change settings $key") preferencesRepository.apply { @@ -51,17 +49,12 @@ class NotificationsPresenter @Inject constructor( view?.openNotificationExactAlarmSettings() } } - notificationsEnableKey -> { - if (isNotificationsEnable && view?.isNotificationPermissionGranted == false) { - view?.requestNotificationPermissions() - } - } isDebugNotificationEnableKey -> { chuckerCollector.showNotification = isDebugNotificationEnable } isNotificationPiggybackEnabledKey -> { - if (isNotificationPiggybackEnabled && view?.isNotificationPiggybackPermissionGranted == false) { - view?.openNotificationPiggyBackPermissionDialog() + if (isNotificationPiggybackEnabled && view?.isNotificationPermissionGranted == false) { + view?.openNotificationPermissionDialog() } } } @@ -77,15 +70,9 @@ class NotificationsPresenter @Inject constructor( view?.openSystemSettings() } - fun onNotificationsPermissionResult() { - view?.run { - setNotificationPreferencesChecked(isNotificationPermissionGranted) - } - } - fun onNotificationPiggybackPermissionResult() { view?.run { - setNotificationPiggybackPreferenceChecked(isNotificationPiggybackPermissionGranted) + setNotificationPiggybackPreferenceChecked(isNotificationPermissionGranted) } } @@ -93,18 +80,10 @@ class NotificationsPresenter @Inject constructor( view?.setUpcomingLessonsNotificationPreferenceChecked(timetableNotificationHelper.canScheduleExactAlarms()) } - private fun checkNotificationsPermissionState() { - if (preferencesRepository.isNotificationsEnable) { - view?.run { - setNotificationPreferencesChecked(isNotificationPermissionGranted) - } - } - } - private fun checkNotificationPiggybackState() { if (preferencesRepository.isNotificationPiggybackEnabled) { view?.run { - setNotificationPiggybackPreferenceChecked(isNotificationPiggybackPermissionGranted) + 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 a391681cb..2bf8e31f4 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 @@ -6,8 +6,6 @@ interface NotificationsView : BaseView { val isNotificationPermissionGranted: Boolean - val isNotificationPiggybackPermissionGranted: Boolean - fun initView(showDebugNotificationSwitch: Boolean) fun showFixSyncDialog() @@ -16,16 +14,10 @@ interface NotificationsView : BaseView { fun enableNotification(notificationKey: String, enable: Boolean) - fun requestNotificationPermissions() - - fun openNotificationsPermissionDialog() - - fun openNotificationPiggyBackPermissionDialog() + fun openNotificationPermissionDialog() fun openNotificationExactAlarmSettings() - fun setNotificationPreferencesChecked(isChecked: Boolean) - fun setNotificationPiggybackPreferenceChecked(isChecked: Boolean) fun setUpcomingLessonsNotificationPreferenceChecked(isChecked: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt index 133b1ff44..8477e3222 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt @@ -5,12 +5,10 @@ import android.os.Bundle import android.view.View import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.ErrorDialog -import io.github.wulkanowy.ui.modules.auth.AuthDialog import io.github.wulkanowy.ui.modules.main.MainView import javax.inject.Inject @@ -52,7 +50,7 @@ class SyncFragment : PreferenceFragmentCompat(), setPreferencesFromResource(R.xml.scheme_preferences_sync, rootKey) } - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { presenter.onSharedPreferenceChanged(key) } @@ -77,19 +75,11 @@ class SyncFragment : PreferenceFragmentCompat(), } override fun showMessage(text: String) { - Snackbar.make(requireView(), text, Snackbar.LENGTH_LONG) - .apply { - anchorView = requireActivity().findViewById(R.id.main_bottom_nav) - show() - } + (activity as? BaseActivity<*, *>)?.showMessage(text) } - override fun showExpiredCredentialsDialog() { - (activity as? BaseActivity<*, *>)?.showExpiredCredentialsDialog() - } - - override fun showDecryptionFailedDialog() { - (activity as? BaseActivity<*, *>)?.showDecryptionFailedDialog() + override fun showExpiredDialog() { + (activity as? BaseActivity<*, *>)?.showExpiredDialog() } override fun showChangePasswordSnackbar(redirectUrl: String) { @@ -104,10 +94,6 @@ class SyncFragment : PreferenceFragmentCompat(), ErrorDialog.newInstance(error).show(childFragmentManager, "error_details") } - override fun showAuthDialog() { - AuthDialog.newInstance().show(childFragmentManager, "auth_dialog") - } - override fun onResume() { super.onResume() preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt index 594e097af..1ecb4a6ef 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 @@ -31,8 +31,7 @@ class SyncPresenter @Inject constructor( setSyncDateInView() } - fun onSharedPreferenceChanged(key: String?) { - key ?: return + fun onSharedPreferenceChanged(key: String) { Timber.i("Change settings $key") preferencesRepository.apply { @@ -53,12 +52,10 @@ class SyncPresenter @Inject constructor( Timber.i("Setting sync now started") analytics.logEvent("sync_now", "status" to "started") } - WorkInfo.State.SUCCEEDED -> { showMessage(syncSuccessString) analytics.logEvent("sync_now", "status" to "success") } - WorkInfo.State.FAILED -> { showError( syncFailedString, @@ -69,7 +66,6 @@ class SyncPresenter @Inject constructor( ) analytics.logEvent("sync_now", "status" to "failed") } - else -> Timber.d("Sync now state: ${workInfo?.state}") } if (workInfo?.state?.isFinished == true) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt index 598046a2d..361a59440 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt @@ -8,7 +8,6 @@ import android.view.MenuInflater import android.view.View import android.widget.Toast import androidx.core.content.getSystemService -import androidx.core.os.bundleOf import androidx.core.view.get import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager @@ -25,8 +24,6 @@ import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.capitalise import io.github.wulkanowy.utils.getThemeAttrColor -import io.github.wulkanowy.utils.nullableSerializable -import io.github.wulkanowy.utils.serializable import javax.inject.Inject @AndroidEntryPoint @@ -41,9 +38,7 @@ class StudentInfoFragment : lateinit var studentInfoAdapter: StudentInfoAdapter override val titleStringId: Int - get() = when ( - requireArguments().nullableSerializable(INFO_TYPE_ARGUMENT_KEY) - ) { + get() = when (requireArguments().getSerializable(INFO_TYPE_ARGUMENT_KEY) as? StudentInfoView.Type) { StudentInfoView.Type.PERSONAL -> R.string.account_personal_data StudentInfoView.Type.CONTACT -> R.string.account_contact StudentInfoView.Type.ADDRESS -> R.string.account_address @@ -63,14 +58,13 @@ class StudentInfoFragment : fun newInstance(type: StudentInfoView.Type, studentWithSemesters: StudentWithSemesters) = StudentInfoFragment().apply { - arguments = bundleOf( - INFO_TYPE_ARGUMENT_KEY to type, - STUDENT_ARGUMENT_KEY to studentWithSemesters - ) + arguments = Bundle().apply { + putSerializable(INFO_TYPE_ARGUMENT_KEY, type) + putSerializable(STUDENT_ARGUMENT_KEY, studentWithSemesters) + } } } - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -80,9 +74,9 @@ class StudentInfoFragment : super.onViewCreated(view, savedInstanceState) binding = FragmentStudentInfoBinding.bind(view) presenter.onAttachView( - view = this, - type = requireArguments().serializable(INFO_TYPE_ARGUMENT_KEY), - studentWithSemesters = requireArguments().serializable(STUDENT_ARGUMENT_KEY), + this, + requireArguments().getSerializable(INFO_TYPE_ARGUMENT_KEY) as StudentInfoView.Type, + requireArguments().getSerializable(STUDENT_ARGUMENT_KEY) as StudentWithSemesters ) } @@ -159,6 +153,7 @@ class StudentInfoFragment : ) } + @OptIn(ExperimentalStdlibApi::class) override fun showFamilyTypeData(studentInfo: StudentInfo) { val items = buildList { add(studentInfo.firstGuardian?.let { 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 a957ef798..d6917672a 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 @@ -12,9 +12,7 @@ import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.databinding.ItemTimetableBinding -import io.github.wulkanowy.databinding.ItemTimetableEmptyBinding import io.github.wulkanowy.databinding.ItemTimetableSmallBinding -import io.github.wulkanowy.utils.getPlural import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.toFormattedString import javax.inject.Inject @@ -31,14 +29,9 @@ class TimetableAdapter @Inject constructor() : TimetableItemType.SMALL -> SmallViewHolder( ItemTimetableSmallBinding.inflate(inflater, parent, false) ) - TimetableItemType.NORMAL -> NormalViewHolder( ItemTimetableBinding.inflate(inflater, parent, false) ) - - TimetableItemType.EMPTY -> EmptyViewHolder( - ItemTimetableEmptyBinding.inflate(inflater, parent, false) - ) } } @@ -47,12 +40,12 @@ class TimetableAdapter @Inject constructor() : position: Int, payloads: MutableList ) { - if (payloads.isNotEmpty() && holder is NormalViewHolder) { - updateTimeLeft( - binding = holder.binding, - timeLeft = (getItem(position) as TimetableItem.Normal).timeLeft, - ) - } else super.onBindViewHolder(holder, position, payloads) + if (payloads.isEmpty()) return super.onBindViewHolder(holder, position, payloads) + + if (holder is NormalViewHolder) updateTimeLeft( + binding = holder.binding, + timeLeft = (getItem(position) as TimetableItem.Normal).timeLeft, + ) } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { @@ -61,16 +54,10 @@ class TimetableAdapter @Inject constructor() : binding = holder.binding, item = getItem(position) as TimetableItem.Small, ) - is NormalViewHolder -> bindNormalView( binding = holder.binding, item = getItem(position) as TimetableItem.Normal, ) - - is EmptyViewHolder -> bindEmptyView( - binding = holder.binding, - item = getItem(position) as TimetableItem.Empty, - ) } } @@ -113,19 +100,6 @@ class TimetableAdapter @Inject constructor() : } } - private fun bindEmptyView(binding: ItemTimetableEmptyBinding, item: TimetableItem.Empty) { - with(binding) { - timetableEmptyItemNumber.text = when (item.numFrom) { - item.numTo -> item.numFrom.toString() - else -> "${item.numFrom}-${item.numTo}" - } - timetableEmptyItemSubject.text = timetableEmptyItemSubject.context.getPlural( - R.plurals.timetable_no_lesson, - item.numTo - item.numFrom + 1 - ) - } - } - private fun updateTimeLeft(binding: ItemTimetableBinding, timeLeft: TimeLeft?) { with(binding) { when { @@ -163,7 +137,6 @@ class TimetableAdapter @Inject constructor() : timetableItemTimeLeft.visibility = VISIBLE timetableItemTimeLeft.text = root.context.getString(R.string.timetable_finished) } - else -> { timetableItemTimeUntil.visibility = GONE timetableItemTimeLeft.visibility = GONE @@ -187,7 +160,7 @@ class TimetableAdapter @Inject constructor() : timetableSmallItemDescription.setTextColor( root.context.getThemeAttrColor( - if (lesson.canceled) R.attr.colorTimetableCanceled + if (lesson.canceled) R.attr.colorPrimary else R.attr.colorTimetableChange ) ) @@ -212,14 +185,13 @@ class TimetableAdapter @Inject constructor() : timetableItemDescription.setTextColor( root.context.getThemeAttrColor( - if (lesson.canceled) R.attr.colorTimetableCanceled + if (lesson.canceled) R.attr.colorPrimary else R.attr.colorTimetableChange ) ) } else { timetableItemDescription.visibility = GONE - timetableItemRoom.isVisible = - lesson.room.isNotBlank() || lesson.roomOld.isNotBlank() + timetableItemRoom.visibility = VISIBLE timetableItemGroup.isVisible = item.showGroupsInPlan && lesson.group.isNotBlank() timetableItemTeacher.visibility = VISIBLE } @@ -256,8 +228,8 @@ class TimetableAdapter @Inject constructor() : } private fun updateNumberAndSubjectCanceledColor(numberView: TextView, subjectView: TextView) { - numberView.setTextColor(numberView.context.getThemeAttrColor(R.attr.colorTimetableCanceled)) - subjectView.setTextColor(subjectView.context.getThemeAttrColor(R.attr.colorTimetableCanceled)) + numberView.setTextColor(numberView.context.getThemeAttrColor(R.attr.colorPrimary)) + subjectView.setTextColor(subjectView.context.getThemeAttrColor(R.attr.colorPrimary)) } private fun updateNumberColor(numberView: TextView, lesson: Timetable) { @@ -302,9 +274,6 @@ class TimetableAdapter @Inject constructor() : private class SmallViewHolder(val binding: ItemTimetableSmallBinding) : RecyclerView.ViewHolder(binding.root) - private class EmptyViewHolder(val binding: ItemTimetableEmptyBinding) : - RecyclerView.ViewHolder(binding.root) - companion object { private val differ = object : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: TimetableItem, newItem: TimetableItem): Boolean = @@ -312,11 +281,9 @@ class TimetableAdapter @Inject constructor() : oldItem is TimetableItem.Small && newItem is TimetableItem.Small -> { oldItem.lesson.start == newItem.lesson.start } - oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal -> { oldItem.lesson.start == newItem.lesson.start } - else -> oldItem == newItem } @@ -325,7 +292,7 @@ class TimetableAdapter @Inject constructor() : override fun getChangePayload(oldItem: TimetableItem, newItem: TimetableItem): Any? { return if (oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal) { - if (oldItem.lesson == newItem.lesson && oldItem.showGroupsInPlan == newItem.showGroupsInPlan && oldItem.timeLeft != newItem.timeLeft) { + if (oldItem.lesson == newItem.lesson && oldItem.timeLeft != newItem.timeLeft) { "time_left" } else super.getChangePayload(oldItem, newItem) } else super.getChangePayload(oldItem, newItem) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt index e8a853479..c9243b12e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt @@ -1,24 +1,26 @@ package io.github.wulkanowy.ui.modules.timetable import android.annotation.SuppressLint -import android.app.Dialog import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG import android.os.Bundle +import android.view.LayoutInflater import android.view.View import android.view.View.GONE import android.view.View.VISIBLE -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.databinding.DialogTimetableBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.capitalise +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.lifecycleAwareVariable +import io.github.wulkanowy.utils.toFormattedString import java.time.Instant -@AndroidEntryPoint -class TimetableDialog : BaseDialogFragment() { +class TimetableDialog : DialogFragment() { + + private var binding: DialogTimetableBinding by lifecycleAwareVariable() private lateinit var lesson: Timetable @@ -26,21 +28,24 @@ class TimetableDialog : BaseDialogFragment() { private const val ARGUMENT_KEY = "Item" - fun newInstance(lesson: Timetable) = TimetableDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to lesson) + fun newInstance(exam: Timetable) = TimetableDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - lesson = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + lesson = getSerializable(ARGUMENT_KEY) as Timetable + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView(DialogTimetableBinding.inflate(layoutInflater).apply { binding = this }.root) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogTimetableBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -81,12 +86,12 @@ class TimetableDialog : BaseDialogFragment() { if (canceled) { timetableDialogChangesTitle.setTextColor( requireContext().getThemeAttrColor( - R.attr.colorTimetableCanceled + R.attr.colorPrimary ) ) timetableDialogChangesValue.setTextColor( requireContext().getThemeAttrColor( - R.attr.colorTimetableCanceled + R.attr.colorPrimary ) ) } else { 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 0e6459110..fdd4aface 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt @@ -7,9 +7,7 @@ import android.view.MenuItem import android.view.View import android.view.View.GONE import android.view.View.VISIBLE -import androidx.core.os.bundleOf import androidx.core.text.parseAsHtml -import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -41,7 +39,9 @@ class TimetableFragment : BaseFragment(R.layout.fragme private const val ARGUMENT_DATE_KEY = "ARGUMENT_DATE" fun newInstance(date: LocalDate? = null) = TimetableFragment().apply { - arguments = date?.let { bundleOf(ARGUMENT_DATE_KEY to it.toEpochDay()) } + arguments = Bundle().apply { + date?.let { putLong(ARGUMENT_DATE_KEY, it.toEpochDay()) } + } } } @@ -51,7 +51,6 @@ class TimetableFragment : BaseFragment(R.layout.fragme override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -88,7 +87,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme timetableNavDate.setOnClickListener { presenter.onPickDate() } timetableNextButton.setOnClickListener { presenter.onNextDay() } - timetableNavContainer.elevation = requireContext().dpToPx(3f) + timetableNavContainer.elevation = requireContext().dpToPx(8f) } } @@ -161,10 +160,6 @@ class TimetableFragment : BaseFragment(R.layout.fragme binding.timetableRecycler.visibility = if (show) VISIBLE else GONE } - override fun showNavigation(show: Boolean) { - binding.timetableNavContainer.isVisible = true - } - override fun showPreButton(show: Boolean) { binding.timetablePreviousButton.visibility = if (show) VISIBLE else View.INVISIBLE } @@ -198,9 +193,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - presenter.currentDate?.toEpochDay()?.let { - outState.putLong(SAVED_DATE_KEY, it) - } + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) } override fun onDestroyView() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt index 105ece38c..92716ace8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt @@ -16,11 +16,6 @@ sealed class TimetableItem(val type: TimetableItemType) { val timeLeft: TimeLeft?, val onClick: (Timetable) -> Unit, ) : TimetableItem(TimetableItemType.NORMAL) - - data class Empty( - val numFrom: Int, - val numTo: Int - ) : TimetableItem(TimetableItemType.EMPTY) } data class TimeLeft( @@ -32,5 +27,4 @@ data class TimeLeft( enum class TimetableItemType { SMALL, NORMAL, - EMPTY } 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 6b442d1c2..d06874082 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -1,52 +1,23 @@ package io.github.wulkanowy.ui.modules.timetable -import android.os.Handler -import android.os.Looper -import io.github.wulkanowy.data.dataOrNull -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.data.enums.TimetableGapsMode.BETWEEN_AND_BEFORE_LESSONS -import io.github.wulkanowy.data.enums.TimetableGapsMode.NO_GAPS import io.github.wulkanowy.data.enums.TimetableMode -import io.github.wulkanowy.data.flatResourceFlow -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.onResourceData -import io.github.wulkanowy.data.onResourceError -import io.github.wulkanowy.data.onResourceIntermediate -import io.github.wulkanowy.data.onResourceNotLoading -import io.github.wulkanowy.data.onResourceSuccess import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.TimetableRepository -import io.github.wulkanowy.data.toFirstResult -import io.github.wulkanowy.data.waitForResult 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.capitalise -import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.isJustFinished -import io.github.wulkanowy.utils.isShowTimeUntil -import io.github.wulkanowy.utils.left -import io.github.wulkanowy.utils.monday -import io.github.wulkanowy.utils.nextOrSameSchoolDay -import io.github.wulkanowy.utils.nextSchoolDay -import io.github.wulkanowy.utils.previousSchoolDay -import io.github.wulkanowy.utils.sunday -import io.github.wulkanowy.utils.toFormattedString -import io.github.wulkanowy.utils.until -import kotlinx.coroutines.flow.firstOrNull +import io.github.wulkanowy.utils.* +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach import timber.log.Timber -import java.time.DayOfWeek import java.time.Instant import java.time.LocalDate -import java.time.LocalDate.now -import java.time.LocalDate.of -import java.time.LocalDate.ofEpochDay -import java.util.Timer +import java.time.LocalDate.* +import java.util.* import javax.inject.Inject import kotlin.concurrent.timer @@ -59,10 +30,9 @@ class TimetablePresenter @Inject constructor( private val analytics: AnalyticsHelper, ) : BasePresenter(errorHandler, studentRepository) { - private var initialDate: LocalDate? = null - private var isWeekendHasLessons: Boolean = false + private var baseDate: LocalDate = now().nextOrSameSchoolDay - var currentDate: LocalDate? = null + lateinit var currentDate: LocalDate private set private lateinit var lastError: Throwable @@ -74,30 +44,23 @@ class TimetablePresenter @Inject constructor( view.initView() Timber.i("Timetable was initialized") errorHandler.showErrorMessage = ::showErrorViewOnError - currentDate = date?.let(::ofEpochDay) + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) loadData() + if (currentDate.isHolidays) setBaseDateOnHolidays() } fun onPreviousDay() { - val date = if (isWeekendHasLessons) { - currentDate?.minusDays(1) - } else currentDate?.previousSchoolDay - - reloadView(date ?: return) + reloadView(currentDate.previousSchoolDay) loadData() } fun onNextDay() { - val date = if (isWeekendHasLessons) { - currentDate?.plusDays(1) - } else currentDate?.nextSchoolDay - - reloadView(date ?: return) + reloadView(currentDate.nextSchoolDay) loadData() } fun onPickDate() { - view?.showDatePickerDialog(currentDate ?: return) + view?.showDatePickerDialog(currentDate) } fun onDateSet(year: Int, month: Int, day: Int) { @@ -126,8 +89,10 @@ class TimetablePresenter @Inject constructor( Timber.i("Timetable view is reselected") view?.let { view -> if (view.currentStackSize == 1) { - if (currentDate != initialDate) { - reloadView(initialDate ?: return) + baseDate = now().nextOrSameSchoolDay + + if (currentDate != baseDate) { + reloadView(baseDate) loadData() } else if (!view.isViewEmpty) { view.resetView() @@ -148,25 +113,34 @@ class TimetablePresenter @Inject constructor( return true } + private fun setBaseDateOnHolidays() { + flow { + val student = studentRepository.getCurrentStudent() + emit(semesterRepository.getCurrentSemester(student)) + }.catch { + Timber.i("Loading semester result: An exception occurred") + }.onEach { + baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear) + currentDate = baseDate + reloadNavigation() + }.launch("holidays") + } + private fun loadData(forceRefresh: Boolean = false) { flatResourceFlow { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) - - checkInitialAndCurrentDate(student, semester) timetableRepository.getTimetable( student = student, semester = semester, - start = currentDate ?: now(), - end = currentDate ?: now(), + start = currentDate, + end = currentDate, forceRefresh = forceRefresh, timetableType = TimetableRepository.TimetableType.NORMAL ) } .logResourceStatus("load timetable data") .onResourceData { - isWeekendHasLessons = isWeekendHasLessons || isWeekendHasLessons(it.lessons) - view?.run { enableSwipe(true) showProgress(false) @@ -174,8 +148,7 @@ class TimetablePresenter @Inject constructor( showContent(it.lessons.isNotEmpty()) showEmpty(it.lessons.isEmpty()) updateData(it.lessons) - setDayHeaderMessage(it.headers.find { header -> header.date == currentDate }?.content) - reloadNavigation() + setDayHeaderMessage(it.headers.singleOrNull { header -> header.date == currentDate }?.content) } } .onResourceIntermediate { view?.showRefresh(true) } @@ -197,54 +170,14 @@ class TimetablePresenter @Inject constructor( .launch() } - private suspend fun checkInitialAndCurrentDate(student: Student, semester: Semester) { - if (initialDate == null) { - val lessons = timetableRepository.getTimetable( - student = student, - semester = semester, - start = now().monday, - end = now().sunday, - forceRefresh = false, - timetableType = TimetableRepository.TimetableType.NORMAL - ).toFirstResult().dataOrNull?.lessons.orEmpty() - isWeekendHasLessons = isWeekendHasLessons(lessons) - initialDate = getInitialDate(semester) - } - - if (currentDate == null) { - currentDate = initialDate - } - } - - private fun isWeekendHasLessons( - lessons: List, - ): Boolean = lessons.any { - it.date.dayOfWeek in listOf( - DayOfWeek.SATURDAY, - DayOfWeek.SUNDAY, - ) - } - - private fun getInitialDate(semester: Semester): LocalDate { - val now = now() - - return when { - now.isHolidays -> now.getLastSchoolDayIfHoliday(semester.schoolYear) - isWeekendHasLessons -> now - else -> now.nextOrSameSchoolDay - } - } - private fun updateData(lessons: List) { tickTimer?.cancel() - if (currentDate != now()) { + if (!prefRepository.showTimetableTimers) { view?.updateData(createItems(lessons)) } else { tickTimer = timer(period = 2_000) { - Handler(Looper.getMainLooper()).post { - view?.updateData(createItems(lessons)) - } + view?.updateData(createItems(lessons)) } } } @@ -259,38 +192,16 @@ class TimetablePresenter @Inject constructor( compareBy({ item -> item.number }, { item -> !item.isStudentPlan }) ) - var prevNum = when (prefRepository.showTimetableGaps) { - BETWEEN_AND_BEFORE_LESSONS -> 0 - else -> null - } - return buildList { - filteredItems.forEachIndexed { i, it -> - if (prefRepository.showTimetableGaps != NO_GAPS && prevNum != null && it.number > prevNum!! + 1) { - val emptyLesson = TimetableItem.Empty( - numFrom = prevNum!! + 1, - numTo = it.number - 1 - ) - add(emptyLesson) - } - - if (it.isStudentPlan) { - val normalLesson = TimetableItem.Normal( - lesson = it, - showGroupsInPlan = prefRepository.showGroupsInPlan, - timeLeft = filteredItems.getTimeLeftForLesson(it, i), - onClick = ::onTimetableItemSelected - ) - add(normalLesson) - } else { - val smallLesson = TimetableItem.Small( - lesson = it, - onClick = ::onTimetableItemSelected - ) - add(smallLesson) - } - - prevNum = it.number - } + return filteredItems.mapIndexed { i, it -> + if (it.isStudentPlan) TimetableItem.Normal( + lesson = it, + showGroupsInPlan = prefRepository.showGroupsInPlan, + timeLeft = filteredItems.getTimeLeftForLesson(it, i), + onClick = ::onTimetableItemSelected + ) else TimetableItem.Small( + lesson = it, + onClick = ::onTimetableItemSelected + ) } } @@ -331,7 +242,7 @@ class TimetablePresenter @Inject constructor( private fun reloadView(date: LocalDate) { currentDate = date - Timber.i("Reload timetable view with the date ${currentDate?.toFormattedString()}") + Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}") view?.apply { showProgress(true) enableSwipe(false) @@ -344,13 +255,10 @@ class TimetablePresenter @Inject constructor( } private fun reloadNavigation() { - val currentDate = currentDate ?: return - view?.apply { showPreButton(!currentDate.minusDays(1).isHolidays) showNextButton(!currentDate.plusDays(1).isHolidays) updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise()) - showNavigation(true) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt index 40190d51f..8cfb26204 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt @@ -36,8 +36,6 @@ interface TimetableView : BaseView { fun showContent(show: Boolean) - fun showNavigation(show: Boolean) - fun showPreButton(show: Boolean) fun showNextButton(show: Boolean) 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 faa833c20..043fa1f7d 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 @@ -2,8 +2,8 @@ package io.github.wulkanowy.ui.modules.timetable.additional import android.os.Bundle import android.view.View +import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.LinearLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.TimetableAdditional @@ -13,7 +13,11 @@ import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.timetable.additional.add.AdditionalLessonAddDialog import io.github.wulkanowy.ui.widgets.DividerItemDecoration -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear +import io.github.wulkanowy.utils.openMaterialDatePicker import java.time.LocalDate import javax.inject.Inject @@ -69,7 +73,7 @@ class AdditionalLessonsFragment : openAddAdditionalLessonButton.setOnClickListener { presenter.onAdditionalLessonAddButtonClicked() } - additionalLessonsNavContainer.elevation = requireContext().dpToPx(3f) + additionalLessonsNavContainer.elevation = requireContext().dpToPx(8f) } } @@ -150,7 +154,7 @@ class AdditionalLessonsFragment : } override fun showDeleteLessonDialog(timetableAdditional: TimetableAdditional) { - MaterialAlertDialogBuilder(requireContext()) + AlertDialog.Builder(requireContext()) .setTitle(getString(R.string.additional_lessons_delete_title)) .setItems( arrayOf( diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt index 134719979..f82d64830 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt @@ -1,10 +1,10 @@ package io.github.wulkanowy.ui.modules.timetable.additional.add -import android.app.Dialog 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.dialog.MaterialAlertDialogBuilder import com.google.android.material.timepicker.MaterialTimePicker import com.google.android.material.timepicker.TimeFormat import dagger.hilt.android.AndroidEntryPoint @@ -29,15 +29,17 @@ class AdditionalLessonAddDialog : BaseDialogFragment fun newInstance() = AdditionalLessonAddDialog() } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView( - DialogAdditionalAddBinding.inflate(layoutInflater).apply { binding = this }.root - ) - .create() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogAdditionalAddBinding.inflate(inflater).apply { binding = this }.root + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) presenter.onAttachView(this) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt index d937d4dd0..7d32278f0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonDialog.kt @@ -1,18 +1,17 @@ package io.github.wulkanowy.ui.modules.timetable.completed -import android.app.Dialog import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.os.bundleOf -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import dagger.hilt.android.AndroidEntryPoint +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.databinding.DialogLessonCompletedBinding -import io.github.wulkanowy.ui.base.BaseDialogFragment -import io.github.wulkanowy.utils.serializable +import io.github.wulkanowy.utils.lifecycleAwareVariable -@AndroidEntryPoint -class CompletedLessonDialog : BaseDialogFragment() { +class CompletedLessonDialog : DialogFragment() { + + private var binding: DialogLessonCompletedBinding by lifecycleAwareVariable() private lateinit var completedLesson: CompletedLesson @@ -20,23 +19,24 @@ class CompletedLessonDialog : BaseDialogFragment() private const val ARGUMENT_KEY = "Item" - fun newInstance(lesson: CompletedLesson) = CompletedLessonDialog().apply { - arguments = bundleOf(ARGUMENT_KEY to lesson) + fun newInstance(exam: CompletedLesson) = CompletedLessonDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - completedLesson = requireArguments().serializable(ARGUMENT_KEY) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + completedLesson = getSerializable(ARGUMENT_KEY) as CompletedLesson + } } - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - return MaterialAlertDialogBuilder(requireContext(), theme) - .setView( - DialogLessonCompletedBinding.inflate(layoutInflater).apply { binding = this }.root - ) - .create() - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ) = DialogLessonCompletedBinding.inflate(inflater).apply { binding = this }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt index 77a7bbd5a..34a69e6ab 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt @@ -2,7 +2,9 @@ package io.github.wulkanowy.ui.modules.timetable.completed import android.os.Bundle import android.view.View -import android.view.View.* +import android.view.View.GONE +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -12,7 +14,12 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.widgets.DividerItemDecoration -import io.github.wulkanowy.utils.* +import io.github.wulkanowy.utils.dpToPx +import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear +import io.github.wulkanowy.utils.getCompatDrawable +import io.github.wulkanowy.utils.getThemeAttrColor +import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear +import io.github.wulkanowy.utils.openMaterialDatePicker import java.time.LocalDate import javax.inject.Inject @@ -66,7 +73,7 @@ class CompletedLessonsFragment : completedLessonsNavDate.setOnClickListener { presenter.onPickDate() } completedLessonsNextButton.setOnClickListener { presenter.onNextDay() } - completedLessonsNavContainer.elevation = requireContext().dpToPx(3f) + completedLessonsNavContainer.elevation = requireContext().dpToPx(8f) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt index 672dbe720..6ef6cfc98 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt @@ -2,11 +2,14 @@ package io.github.wulkanowy.ui.modules.timetablewidget import android.appwidget.AppWidgetManager.* import android.content.Intent +import android.os.Build import android.os.Bundle import android.widget.Toast import android.widget.Toast.LENGTH_LONG +import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.databinding.ActivityWidgetConfigureBinding import io.github.wulkanowy.ui.base.BaseActivity @@ -31,6 +34,8 @@ class TimetableWidgetConfigureActivity : @Inject lateinit var appInfo: AppInfo + private var dialog: AlertDialog? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setResult(RESULT_CANCELED) @@ -56,6 +61,23 @@ class TimetableWidgetConfigureActivity : configureAdapter.onClickListener = presenter::onItemSelect } + override fun showThemeDialog() { + var items = arrayOf( + getString(R.string.widget_timetable_theme_light), + getString(R.string.widget_timetable_theme_dark) + ) + + if (appInfo.systemVersion >= Build.VERSION_CODES.Q) items += getString(R.string.widget_timetable_theme_system) + + dialog = AlertDialog.Builder(this, R.style.WulkanowyTheme_WidgetAccountSwitcher) + .setTitle(R.string.widget_timetable_theme_title) + .setOnDismissListener { presenter.onDismissThemeView() } + .setSingleChoiceItems(items, -1) { _, which -> + presenter.onThemeSelect(which) + } + .show() + } + override fun updateData(data: List, selectedStudentId: Long) { with(configureAdapter) { selectedId = selectedStudentId @@ -88,4 +110,9 @@ class TimetableWidgetConfigureActivity : override fun openLoginView() { startActivity(LoginActivity.getStartIntent(this)) } + + override fun onDestroy() { + super.onDestroy() + dialog?.dismiss() + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt index 87e89336c..dc2a7c6c7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt @@ -8,6 +8,7 @@ import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getThemeWidgetKey import kotlinx.coroutines.flow.onEach import timber.log.Timber import javax.inject.Inject @@ -38,9 +39,22 @@ class TimetableWidgetConfigurePresenter @Inject constructor( fun onItemSelect(student: Student) { selectedStudent = student + + if (isFromProvider) registerStudent(selectedStudent) + else view?.showThemeDialog() + } + + fun onThemeSelect(index: Int) { + appWidgetId?.let { + sharedPref.putLong(getThemeWidgetKey(it), index.toLong()) + } registerStudent(selectedStudent) } + fun onDismissThemeView() { + view?.finishView() + } + private fun loadData() { resourceFlow { studentRepository.getSavedStudents(false) }.onEach { when (it) { @@ -51,7 +65,10 @@ class TimetableWidgetConfigurePresenter @Inject constructor( } ?: -1 when { it.data.isEmpty() -> view?.openLoginView() - it.data.size == 1 && !isFromProvider -> onItemSelect(it.data.single().student) + it.data.size == 1 && !isFromProvider -> { + selectedStudent = it.data.single().student + view?.showThemeDialog() + } else -> view?.updateData(it.data, selectedStudentId) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt index 7740b9bbe..accdc28dc 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureView.kt @@ -11,6 +11,8 @@ interface TimetableWidgetConfigureView : BaseView { fun updateTimetableWidget(widgetId: Int) + fun showThemeDialog() + fun setSuccessResult(widgetId: Int) fun finishView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt index 4e0578e2b..664086bca 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt @@ -1,52 +1,51 @@ package io.github.wulkanowy.ui.modules.timetablewidget +import android.annotation.SuppressLint import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID import android.content.Context import android.content.Intent -import android.content.res.Configuration import android.graphics.Paint.ANTI_ALIAS_FLAG import android.graphics.Paint.STRIKE_THRU_TEXT_FLAG import android.view.View.GONE import android.view.View.VISIBLE +import android.widget.AdapterView.INVALID_POSITION import android.widget.RemoteViews import android.widget.RemoteViewsService import io.github.wulkanowy.R import io.github.wulkanowy.data.dataOrNull import io.github.wulkanowy.data.db.SharedPrefProvider -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.data.enums.TimetableGapsMode.BETWEEN_AND_BEFORE_LESSONS -import io.github.wulkanowy.data.enums.TimetableGapsMode.NO_GAPS +import io.github.wulkanowy.data.enums.TimetableMode import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.data.toFirstResult +import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getCurrentThemeWidgetKey import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getTodayLastLessonEndDateTimeWidgetKey import io.github.wulkanowy.utils.getCompatColor -import io.github.wulkanowy.utils.getPlural import io.github.wulkanowy.utils.toFormattedString import kotlinx.coroutines.runBlocking import timber.log.Timber -import java.time.Instant import java.time.LocalDate class TimetableWidgetFactory( private val timetableRepository: TimetableRepository, private val studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, - private val sharedPref: SharedPrefProvider, private val prefRepository: PreferencesRepository, + private val sharedPref: SharedPrefProvider, private val context: Context, private val intent: Intent? ) : RemoteViewsService.RemoteViewsFactory { - private var items = emptyList() + private var lessons = emptyList() - private var timetableCanceledColor: Int? = null + private var savedCurrentTheme: Long? = null + + private var primaryColor: Int? = null private var textColor: Int? = null @@ -56,9 +55,9 @@ class TimetableWidgetFactory( override fun hasStableIds() = true - override fun getCount() = items.size + override fun getCount() = lessons.size - override fun getViewTypeCount() = 3 + override fun getViewTypeCount() = 2 override fun getItemId(position: Int) = position.toLong() @@ -71,233 +70,195 @@ class TimetableWidgetFactory( val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0)) val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0) - runCatching { - runBlocking { - val student = getStudent(studentId) ?: return@runBlocking - val semester = semesterRepository.getCurrentSemester(student) - items = createItems( - lessons = getLessons(student, semester, date), - lastSync = timetableRepository.getLastRefreshTimestamp(semester, date, date) - ) - if (date == LocalDate.now()) { - updateTodayLastLessonEnd(appWidgetId) - } - } - }.onFailure { - Timber.e(it, "An error has occurred in timetable widget factory") + updateTheme(appWidgetId) + lessons = getLessons(date, studentId) + + val todayLastLessonEndTimestamp = lessons.maxOfOrNull { it.end } + if (date == LocalDate.now() && todayLastLessonEndTimestamp != null) { + sharedPref.putLong( + key = getTodayLastLessonEndDateTimeWidgetKey(appWidgetId), + value = todayLastLessonEndTimestamp.epochSecond, + sync = true + ) } } } - private suspend fun getStudent(studentId: Long): Student? { - val students = studentRepository.getSavedStudents() - return students.singleOrNull { it.student.id == studentId }?.student - } + private fun updateTheme(appWidgetId: Int) { + savedCurrentTheme = sharedPref.getLong(getCurrentThemeWidgetKey(appWidgetId), 0) - private suspend fun getLessons( - student: Student, semester: Semester, date: LocalDate - ): List { - val timetable = timetableRepository.getTimetable(student, semester, date, date, false) - val lessons = timetable.toFirstResult().dataOrNull?.lessons.orEmpty() - return lessons.sortedBy { it.number } - } - - private fun createItems( - lessons: List, - lastSync: Instant?, - ): List { - var prevNum = when (prefRepository.showTimetableGaps) { - BETWEEN_AND_BEFORE_LESSONS -> 0 - else -> null + if (savedCurrentTheme == 0L) { + primaryColor = R.color.colorPrimary + textColor = android.R.color.black + timetableChangeColor = R.color.timetable_change_dark + } else { + primaryColor = R.color.colorPrimaryLight + textColor = android.R.color.white + timetableChangeColor = R.color.timetable_change_light } - return buildList { - lessons.forEach { - if (prefRepository.showTimetableGaps != NO_GAPS && prevNum != null && it.number > prevNum!! + 1) { - val emptyItem = TimetableWidgetItem.Empty( - numFrom = prevNum!! + 1, - numTo = it.number - 1 - ) - add(emptyItem) - } - add(TimetableWidgetItem.Normal(it)) - prevNum = it.number + } + + private fun getItemLayout(lesson: Timetable): Int { + return when { + prefRepository.showWholeClassPlan == TimetableMode.SMALL_OTHER_GROUP && !lesson.isStudentPlan -> { + if (savedCurrentTheme == 0L) R.layout.item_widget_timetable_small + else R.layout.item_widget_timetable_small_dark } - add(TimetableWidgetItem.Synchronized(lastSync ?: Instant.MIN)) + savedCurrentTheme == 1L -> R.layout.item_widget_timetable_dark + else -> R.layout.item_widget_timetable } } - private fun updateTodayLastLessonEnd(appWidgetId: Int) { - val todayLastLessonEnd = items.filterIsInstance() - .maxOfOrNull { it.lesson.end } ?: return - val key = getTodayLastLessonEndDateTimeWidgetKey(appWidgetId) - sharedPref.putLong(key, todayLastLessonEnd.epochSecond, true) - } - - companion object { - const val TIME_FORMAT_STYLE = "HH:mm" + private fun getLessons(date: LocalDate, studentId: Long) = try { + runBlocking { + if (!studentRepository.isStudentSaved()) return@runBlocking emptyList() + + val students = studentRepository.getSavedStudents() + val student = students.singleOrNull { it.student.id == studentId }?.student + ?: return@runBlocking emptyList() + + val semester = semesterRepository.getCurrentSemester(student) + timetableRepository.getTimetable(student, semester, date, date, false) + .toFirstResult().dataOrNull?.lessons.orEmpty() + .sortedWith(compareBy({ it.number }, { !it.isStudentPlan })) + .filter { + if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) { + it.isStudentPlan + } else true + } + } + } catch (e: Exception) { + Timber.e(e, "An error has occurred in timetable widget factory") + emptyList() } + @SuppressLint("DefaultLocale") override fun getViewAt(position: Int): RemoteViews? { - return when (val item = items.getOrNull(position) ?: return null) { - is TimetableWidgetItem.Normal -> getNormalItemRemoteView(item) - is TimetableWidgetItem.Empty -> getEmptyItemRemoteView(item) - is TimetableWidgetItem.Synchronized -> getSynchronizedItemRemoteView(item) - } - } + if (position == INVALID_POSITION || lessons.getOrNull(position) == null) return null - private fun getNormalItemRemoteView(item: TimetableWidgetItem.Normal): RemoteViews { - val lesson = item.lesson - - val lessonStartTime = lesson.start.toFormattedString(TIME_FORMAT_STYLE) - val lessonEndTime = lesson.end.toFormattedString(TIME_FORMAT_STYLE) - - val remoteViews = RemoteViews(context.packageName, R.layout.item_widget_timetable).apply { - setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString()) - setTextViewText(R.id.timetableWidgetItemTimeStart, lessonStartTime) - setTextViewText(R.id.timetableWidgetItemTimeFinish, lessonEndTime) + val lesson = lessons[position] + return RemoteViews(context.packageName, getItemLayout(lesson)).apply { setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject) + setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString()) + setTextViewText( + R.id.timetableWidgetItemTimeStart, + lesson.start.toFormattedString("HH:mm") + ) + setTextViewText( + R.id.timetableWidgetItemTimeFinish, + lesson.end.toFormattedString("HH:mm") + ) + + updateDescription(this, lesson) + + if (lesson.canceled) { + updateStylesCanceled(this) + } else { + updateStylesNotCanceled(this, lesson) + } - setTextViewText(R.id.timetableWidgetItemTeacher, lesson.teacher) - setTextViewText(R.id.timetableWidgetItemDescription, lesson.info) setOnClickFillInIntent(R.id.timetableWidgetItemContainer, Intent()) } - updateTheme() - clearLessonStyles(remoteViews) - if (lesson.room.isBlank()) { - remoteViews.setViewVisibility(R.id.timetableWidgetItemRoom, GONE) - } else { - remoteViews.setTextViewText(R.id.timetableWidgetItemRoom, lesson.room) - } - when { - lesson.canceled -> applyCancelledLessonStyles(remoteViews) - lesson.changes or lesson.info.isNotBlank() -> applyChangedLessonStyles( - remoteViews = remoteViews, - lesson = lesson, - ) - } - return remoteViews } - private fun getEmptyItemRemoteView(item: TimetableWidgetItem.Empty): RemoteViews { - return RemoteViews( - context.packageName, - R.layout.item_widget_timetable_empty - ).apply { - setTextViewText( - R.id.timetableWidgetEmptyItemNumber, - when (item.numFrom) { - item.numTo -> item.numFrom.toString() - else -> "${item.numFrom}-${item.numTo}" - } - ) - setTextViewText( - R.id.timetableWidgetEmptyItemText, - context.getPlural( - R.plurals.timetable_no_lesson, - item.numTo - item.numFrom + 1 - ) - ) - setOnClickFillInIntent(R.id.timetableWidgetEmptyItemContainer, Intent()) - } - } - - private fun getSynchronizedItemRemoteView(item: TimetableWidgetItem.Synchronized): RemoteViews { - return RemoteViews( - context.packageName, - R.layout.item_widget_timetable_footer - ).apply { - setTextViewText( - R.id.timetableWidgetSynchronizationTime, - getSynchronizationInfoText(item.timestamp) - ) - } - } - - private fun updateTheme() { - when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) { - Configuration.UI_MODE_NIGHT_YES -> { - textColor = android.R.color.white - timetableChangeColor = R.color.timetable_change_dark - timetableCanceledColor = R.color.timetable_canceled_dark - } - - else -> { - textColor = android.R.color.black - timetableChangeColor = R.color.timetable_change_light - timetableCanceledColor = R.color.timetable_canceled_light + private fun updateDescription(remoteViews: RemoteViews, lesson: Timetable) { + with(remoteViews) { + if (lesson.info.isNotBlank() && !lesson.changes) { + setTextViewText(R.id.timetableWidgetItemDescription, lesson.info) + setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE) + setViewVisibility(R.id.timetableWidgetItemRoom, GONE) + setViewVisibility(R.id.timetableWidgetItemTeacher, GONE) + } else { + setViewVisibility(R.id.timetableWidgetItemDescription, GONE) + setViewVisibility(R.id.timetableWidgetItemRoom, VISIBLE) + setViewVisibility(R.id.timetableWidgetItemTeacher, VISIBLE) } } } - private fun clearLessonStyles(remoteViews: RemoteViews) { - val defaultTextColor = context.getCompatColor(textColor ?: 0) + private fun updateStylesCanceled(remoteViews: RemoteViews) { + with(remoteViews) { + setInt( + R.id.timetableWidgetItemSubject, "setPaintFlags", + STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG + ) + setTextColor(R.id.timetableWidgetItemNumber, context.getCompatColor(primaryColor!!)) + setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor(primaryColor!!)) + setTextColor( + R.id.timetableWidgetItemDescription, + context.getCompatColor(primaryColor!!) + ) + } + } - remoteViews.apply { + private fun updateStylesNotCanceled(remoteViews: RemoteViews, lesson: Timetable) { + with(remoteViews) { setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", ANTI_ALIAS_FLAG) - setViewVisibility(R.id.timetableWidgetItemRoom, VISIBLE) - setViewVisibility(R.id.timetableWidgetItemTeacher, VISIBLE) - setViewVisibility(R.id.timetableWidgetItemIcon, GONE) - setViewVisibility(R.id.timetableWidgetItemDescription, GONE) - setTextColor(R.id.timetableWidgetItemNumber, defaultTextColor) - setTextColor(R.id.timetableWidgetItemSubject, defaultTextColor) - setTextColor(R.id.timetableWidgetItemRoom, defaultTextColor) - setTextColor(R.id.timetableWidgetItemTeacher, defaultTextColor) - setTextColor(R.id.timetableWidgetItemDescription, defaultTextColor) - } - } - - private fun applyCancelledLessonStyles(remoteViews: RemoteViews) { - val cancelledThemeColor = context.getCompatColor(timetableCanceledColor ?: 0) - val strikeThroughPaintFlags = STRIKE_THRU_TEXT_FLAG or ANTI_ALIAS_FLAG - - remoteViews.apply { - setInt(R.id.timetableWidgetItemSubject, "setPaintFlags", strikeThroughPaintFlags) - setTextColor(R.id.timetableWidgetItemNumber, cancelledThemeColor) - setTextColor(R.id.timetableWidgetItemSubject, cancelledThemeColor) - setTextColor(R.id.timetableWidgetItemDescription, cancelledThemeColor) - setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE) - setViewVisibility(R.id.timetableWidgetItemRoom, GONE) - setViewVisibility(R.id.timetableWidgetItemTeacher, GONE) - } - } - - private fun applyChangedLessonStyles(remoteViews: RemoteViews, lesson: Timetable) { - val changesTextColor = context.getCompatColor(timetableChangeColor ?: 0) - - remoteViews.apply { - setTextColor(R.id.timetableWidgetItemNumber, changesTextColor) - setTextColor(R.id.timetableWidgetItemDescription, changesTextColor) - setViewVisibility(R.id.timetableWidgetItemIcon, VISIBLE) - setImageViewResource(R.id.timetableWidgetItemIcon, R.drawable.ic_timetable_widget_swap) - } - - if (lesson.subject != lesson.subjectOld) { - remoteViews.setTextColor(R.id.timetableWidgetItemSubject, changesTextColor) - } - - if (lesson.room != lesson.roomOld) { - remoteViews.setTextColor(R.id.timetableWidgetItemRoom, changesTextColor) - } - - if (lesson.teacher != lesson.teacherOld) { - remoteViews.setTextColor(R.id.timetableWidgetItemTeacher, changesTextColor) - } - - if (lesson.info.isNotBlank() && !lesson.changes) { - remoteViews.setViewVisibility(R.id.timetableWidgetItemDescription, VISIBLE) - remoteViews.setViewVisibility(R.id.timetableWidgetItemRoom, GONE) - remoteViews.setViewVisibility(R.id.timetableWidgetItemTeacher, GONE) - } - } - - private fun getSynchronizationInfoText(synchronizationInstant: Instant) = - synchronizationInstant.run { - val synchronizationTime = toFormattedString(TIME_FORMAT_STYLE) - val synchronizationDate = toFormattedString() - context.getString( - R.string.widget_timetable_last_synchronization, - synchronizationDate, - synchronizationTime, + setTextColor(R.id.timetableWidgetItemSubject, context.getCompatColor(textColor!!)) + setTextColor( + R.id.timetableWidgetItemDescription, + context.getCompatColor(timetableChangeColor!!) ) + + updateNotCanceledLessonNumberColor(this, lesson) + updateNotCanceledSubjectColor(this, lesson) + + val teacherChange = lesson.teacherOld.isNotBlank() + updateNotCanceledRoom(this, lesson, teacherChange) + updateNotCanceledTeacher(this, lesson, teacherChange) } + } + + private fun updateNotCanceledLessonNumberColor(remoteViews: RemoteViews, lesson: Timetable) { + remoteViews.setTextColor( + R.id.timetableWidgetItemNumber, context.getCompatColor( + if (lesson.changes || (lesson.info.isNotBlank() && !lesson.canceled)) timetableChangeColor!! + else textColor!! + ) + ) + } + + private fun updateNotCanceledSubjectColor(remoteViews: RemoteViews, lesson: Timetable) { + remoteViews.setTextColor( + R.id.timetableWidgetItemSubject, context.getCompatColor( + if (lesson.subjectOld.isNotBlank() && lesson.subject != lesson.subjectOld) timetableChangeColor!! + else textColor!! + ) + ) + } + + private fun updateNotCanceledRoom( + remoteViews: RemoteViews, + lesson: Timetable, + teacherChange: Boolean + ) { + with(remoteViews) { + if (lesson.room.isNotBlank()) { + setTextViewText( + R.id.timetableWidgetItemRoom, + if (teacherChange) lesson.room + else "${context.getString(R.string.timetable_room)} ${lesson.room}" + ) + + setTextColor( + R.id.timetableWidgetItemRoom, context.getCompatColor( + if (lesson.roomOld.isNotBlank() && lesson.room != lesson.roomOld) timetableChangeColor!! + else textColor!! + ) + ) + } else setTextViewText(R.id.timetableWidgetItemRoom, "") + } + } + + private fun updateNotCanceledTeacher( + remoteViews: RemoteViews, + lesson: Timetable, + teacherChange: Boolean + ) { + remoteViews.setTextViewText( + R.id.timetableWidgetItemTeacher, + if (teacherChange) lesson.teacher + else "" + ) + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetItem.kt deleted file mode 100644 index 166b1a8fb..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetItem.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.ui.modules.timetablewidget - -import io.github.wulkanowy.data.db.entities.Timetable -import java.time.Instant - -sealed class TimetableWidgetItem(val type: TimetableWidgetItemType) { - - data class Normal( - val lesson: Timetable, - ) : TimetableWidgetItem(TimetableWidgetItemType.NORMAL) - - data class Empty( - val numFrom: Int, - val numTo: Int - ) : TimetableWidgetItem(TimetableWidgetItemType.EMPTY) - - data class Synchronized( - val timestamp: Instant, - ) : TimetableWidgetItem(TimetableWidgetItemType.SYNCHRONIZED) -} - -enum class TimetableWidgetItemType { - NORMAL, - EMPTY, - SYNCHRONIZED, -} 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 cc48539a5..3ba2ae946 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 @@ -8,11 +8,10 @@ import android.content.Context import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.content.res.Configuration import android.graphics.Bitmap +import android.graphics.Canvas import android.widget.RemoteViews -import androidx.appcompat.content.res.AppCompatResources -import androidx.core.graphics.drawable.DrawableCompat -import androidx.core.graphics.drawable.toBitmap import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.SharedPrefProvider @@ -71,177 +70,175 @@ class TimetableWidgetProvider : BroadcastReceiver() { "timetable_widget_today_last_lesson_end_date_time_$appWidgetId" fun getStudentWidgetKey(appWidgetId: Int) = "timetable_widget_student_$appWidgetId" + + fun getThemeWidgetKey(appWidgetId: Int) = "timetable_widget_theme_$appWidgetId" + + fun getCurrentThemeWidgetKey(appWidgetId: Int) = + "timetable_widget_current_theme_$appWidgetId" } @OptIn(DelicateCoroutinesApi::class) override fun onReceive(context: Context, intent: Intent) { GlobalScope.launch { when (intent.action) { - ACTION_APPWIDGET_UPDATE -> onWidgetUpdate(context, intent) - ACTION_APPWIDGET_DELETED -> onWidgetDeleted(intent) + ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent) + ACTION_APPWIDGET_DELETED -> onDelete(intent) } } } - private suspend fun onWidgetUpdate(context: Context, intent: Intent) { - val pressedButton = intent.getPressedButton() + private suspend fun onUpdate(context: Context, intent: Intent) { + if (intent.getStringExtra(EXTRA_BUTTON_TYPE) == null) { + val isFromConfigure = intent.getBooleanExtra(EXTRA_FROM_CONFIGURE, false) + val appWidgetIds = intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS) ?: return - if (pressedButton == null) { - val updatedWidgetIds = intent.getWidgetIds() ?: return - updatedWidgetIds.forEach { updateWidgetLayout(context, it) } + appWidgetIds.forEach { appWidgetId -> + val student = + getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId) + val savedDataEpochDay = sharedPref.getLong(getDateWidgetKey(appWidgetId), 0) + + val dateToLoad = if (isFromConfigure && savedDataEpochDay != 0L) { + LocalDate.ofEpochDay(savedDataEpochDay) + } else { + getWidgetDefaultDateToLoad(appWidgetId) + } + + updateWidget(context, appWidgetId, dateToLoad, student) + } } else { - val widgetId = intent.getToggledWidgetId() ?: return - reportChangedDay(pressedButton) - updateSavedWidgetDate(widgetId, pressedButton) - updateWidgetLayout(context, widgetId) + val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE) + val toggledWidgetId = intent.getIntExtra(EXTRA_TOGGLED_WIDGET_ID, 0) + val student = getStudent( + sharedPref.getLong(getStudentWidgetKey(toggledWidgetId), 0), + toggledWidgetId + ) + val savedDate = + LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0)) + val date = when (buttonType) { + BUTTON_RESET -> getWidgetDefaultDateToLoad(toggledWidgetId) + BUTTON_NEXT -> savedDate.nextSchoolDay + BUTTON_PREV -> savedDate.previousSchoolDay + else -> getWidgetDefaultDateToLoad(toggledWidgetId) + } + if (!buttonType.isNullOrBlank()) { + analytics.logEvent( + "changed_timetable_widget_day", + "button" to buttonType + ) + } + updateWidget(context, toggledWidgetId, date, student) } } - private fun Intent.getPressedButton(): String? { - return getStringExtra(EXTRA_BUTTON_TYPE) - } + private fun onDelete(intent: Intent) { + val appWidgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, 0) - private fun Intent.getWidgetIds(): IntArray? { - return getIntArrayExtra(EXTRA_APPWIDGET_IDS) - } - - private fun Intent.getToggledWidgetId(): Int? { - val toggledWidgetId = getIntExtra(EXTRA_TOGGLED_WIDGET_ID, INVALID_APPWIDGET_ID) - return toggledWidgetId.takeIf { it != INVALID_APPWIDGET_ID } - } - - private fun reportChangedDay(buttonType: String) { - if (buttonType.isNotBlank()) { - analytics.logEvent("changed_timetable_widget_day", "button" to buttonType) + if (appWidgetId != 0) { + with(sharedPref) { + delete(getStudentWidgetKey(appWidgetId)) + delete(getDateWidgetKey(appWidgetId)) + delete(getThemeWidgetKey(appWidgetId)) + delete(getCurrentThemeWidgetKey(appWidgetId)) + } } } - private fun updateSavedWidgetDate(widgetId: Int, buttonType: String) { - val savedDate = getSavedWidgetDate(widgetId) - val newDate = savedDate?.let { getNewDate(it, widgetId, buttonType) } - ?: getWidgetDefaultDateToLoad(widgetId) - setWidgetDate(widgetId, newDate) - } - - private fun getSavedWidgetDate(widgetId: Int): LocalDate? { - val epochDay = sharedPref.getLong(getDateWidgetKey(widgetId), 0) - return if (epochDay == 0L) null else LocalDate.ofEpochDay(epochDay) - } - - private fun getNewDate( - currentDate: LocalDate, - widgetId: Int, - selectedButton: String - ): LocalDate { - return when (selectedButton) { - BUTTON_NEXT -> currentDate.nextSchoolDay - BUTTON_PREV -> currentDate.previousSchoolDay - else -> getWidgetDefaultDateToLoad(widgetId) - } - } - - private fun setWidgetDate(widgetId: Int, dateToSet: LocalDate) { - val widgetDateKey = getDateWidgetKey(widgetId) - sharedPref.putLong(widgetDateKey, dateToSet.toEpochDay(), true) - } - - private fun getWidgetDefaultDateToLoad(widgetId: Int): LocalDate { - val lastLessonEndDateTime = getLastLessonDateTime(widgetId) - - val todayDate = LocalDate.now() - val isLastLessonToday = lastLessonEndDateTime.toLocalDate() == todayDate - val isEndOfLessons = LocalDateTime.now() > lastLessonEndDateTime - - return if (isLastLessonToday && isEndOfLessons) { - todayDate.nextSchoolDay - } else { - todayDate.nextOrSameSchoolDay - } - } - - private fun getLastLessonDateTime(widgetId: Int): LocalDateTime { - val lastLessonTimestamp = sharedPref - .getLong(getTodayLastLessonEndDateTimeWidgetKey(widgetId), 0) - return LocalDateTime.ofEpochSecond(lastLessonTimestamp, 0, ZoneOffset.UTC) - } - - private suspend fun updateWidgetLayout( - context: Context, widgetId: Int + private fun updateWidget( + context: Context, + appWidgetId: Int, + date: LocalDate, + student: Student? ) { - val widgetRemoteViews = RemoteViews(context.packageName, R.layout.widget_timetable) + val savedConfigureTheme = sharedPref.getLong(getThemeWidgetKey(appWidgetId), 0) + val isSystemDarkMode = + context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES + var currentTheme = 0L + var layoutId = R.layout.widget_timetable - // Apply the click action intent - val appIntent = createPendingAppIntent(context) - widgetRemoteViews.setPendingIntentTemplate(R.id.timetableWidgetList, appIntent) + if (savedConfigureTheme == 1L || (savedConfigureTheme == 2L && isSystemDarkMode)) { + currentTheme = 1L + layoutId = R.layout.widget_timetable_dark + } - // Display saved date - val date = getSavedWidgetDate(widgetId) ?: getWidgetDefaultDateToLoad(widgetId) - val formattedDate = date.toFormattedString("EEE, dd.MM").capitalise() - widgetRemoteViews.setTextViewText(R.id.timetableWidgetDate, formattedDate) - - // Apply intents to the date switcher buttons - val nextNavIntent = createNavButtonIntent(context, widgetId, widgetId, BUTTON_NEXT) - val prevNavIntent = createNavButtonIntent(context, -widgetId, widgetId, BUTTON_PREV) + val nextNavIntent = createNavIntent(context, appWidgetId, appWidgetId, BUTTON_NEXT) + val prevNavIntent = createNavIntent(context, -appWidgetId, appWidgetId, BUTTON_PREV) val resetNavIntent = - createNavButtonIntent(context, Int.MAX_VALUE - widgetId, widgetId, BUTTON_RESET) - widgetRemoteViews.run { + createNavIntent(context, Int.MAX_VALUE - appWidgetId, appWidgetId, BUTTON_RESET) + val adapterIntent = Intent(context, TimetableWidgetService::class.java) + .apply { + putExtra(EXTRA_APPWIDGET_ID, appWidgetId) + //make Intent unique + action = appWidgetId.toString() + } + val accountIntent = PendingIntent.getActivity( + context, + -Int.MAX_VALUE + appWidgetId, + Intent(context, TimetableWidgetConfigureActivity::class.java).apply { + addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK) + putExtra(EXTRA_APPWIDGET_ID, appWidgetId) + putExtra(EXTRA_FROM_PROVIDER, true) + }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + + ) + val appIntent = PendingIntent.getActivity( + context, + TIMETABLE_PENDING_INTENT_ID, + SplashActivity.getStartIntent(context, Destination.Timetable()), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE + ) + + val remoteView = RemoteViews(context.packageName, layoutId).apply { + setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty) + setTextViewText( + R.id.timetableWidgetDate, + date.toFormattedString("EEEE, dd.MM").capitalise() + ) + setTextViewText( + R.id.timetableWidgetName, + student?.nickOrName ?: context.getString(R.string.all_no_data) + ) + + student?.let { + setImageViewBitmap(R.id.timetableWidgetAccount, context.createAvatarBitmap(it)) + } + + setRemoteAdapter(R.id.timetableWidgetList, adapterIntent) setOnClickPendingIntent(R.id.timetableWidgetNext, nextNavIntent) setOnClickPendingIntent(R.id.timetableWidgetPrev, prevNavIntent) setOnClickPendingIntent(R.id.timetableWidgetDate, resetNavIntent) + setOnClickPendingIntent(R.id.timetableWidgetName, resetNavIntent) + setOnClickPendingIntent(R.id.timetableWidgetAccount, accountIntent) + setPendingIntentTemplate(R.id.timetableWidgetList, appIntent) } - // Setup the lesson list adapter - val lessonListAdapterIntent = createLessonListAdapterIntent(context, widgetId) - // --- Ensure the selected date is stored in the shared preferences, - // --- on which the TimetableWidgetFactory relies - setWidgetDate(widgetId, date) - // --- - widgetRemoteViews.apply { - setEmptyView(R.id.timetableWidgetList, R.id.timetableWidgetEmpty) - setRemoteAdapter(R.id.timetableWidgetList, lessonListAdapterIntent) + with(sharedPref) { + putLong(getCurrentThemeWidgetKey(appWidgetId), currentTheme) + putLong(getDateWidgetKey(appWidgetId), date.toEpochDay(), true) } - // Setup profile picture - getWidgetStudent(widgetId)?.let { student -> - setupAccountView(context, student, widgetRemoteViews, widgetId) - } - - // Apply updates with(appWidgetManager) { - partiallyUpdateAppWidget(widgetId, widgetRemoteViews) - notifyAppWidgetViewDataChanged(widgetId, R.id.timetableWidgetList) + updateAppWidget(appWidgetId, remoteView) + notifyAppWidgetViewDataChanged(appWidgetId, R.id.timetableWidgetList) + Timber.d("TimetableWidgetProvider updated") } - - Timber.d("TimetableWidgetProvider updated") } - private fun createPendingAppIntent(context: Context) = PendingIntent.getActivity( - context, TIMETABLE_PENDING_INTENT_ID, - SplashActivity.getStartIntent(context, Destination.Timetable()), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) - - private fun createNavButtonIntent( - context: Context, code: Int, appWidgetId: Int, buttonType: String + private fun createNavIntent( + context: Context, + code: Int, + appWidgetId: Int, + buttonType: String ) = PendingIntent.getBroadcast( - context, code, Intent(context, TimetableWidgetProvider::class.java).apply { + 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.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE ) - private fun createLessonListAdapterIntent(context: Context, widgetId: Int) = - Intent(context, TimetableWidgetService::class.java).apply { - putExtra(EXTRA_APPWIDGET_ID, widgetId) - action = widgetId.toString() //make Intent unique - } - - private suspend fun getWidgetStudent(widgetId: Int): Student? { - val studentId = sharedPref.getLong(getStudentWidgetKey(widgetId), 0) - return getStudent(studentId, widgetId) - } - private suspend fun getStudent(studentId: Long, appWidgetId: Int) = try { val students = studentRepository.getSavedStudents(false) val student = students.singleOrNull { it.student.id == studentId }?.student @@ -252,7 +249,6 @@ class TimetableWidgetProvider : BroadcastReceiver() { sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id) } } - else -> null } } catch (e: Exception) { @@ -262,64 +258,45 @@ class TimetableWidgetProvider : BroadcastReceiver() { null } - private fun setupAccountView( - context: Context, student: Student, remoteViews: RemoteViews, widgetId: Int - ) { - val accountInitials = getAccountInitials(student.nickOrName) - val accountPickerPendingIntent = createAccountPickerPendingIntent(context, widgetId) - - getAvatarBackgroundBitmap(context, student.avatarColor)?.let { - remoteViews.setImageViewBitmap(R.id.timetableWidgetAccountBackground, it) + private fun Context.createAvatarBitmap(student: Student): Bitmap { + val avatarColor = if (student.avatarColor == -2937041L) { + getCompatColor(R.color.colorPrimaryLight).toLong() + } else { + student.avatarColor } + val avatarDrawable = createNameInitialsDrawable(student.nickOrName, avatarColor, 0.5f) - remoteViews.apply { - setTextViewText(R.id.timetableWidgetAccountInitials, accountInitials) - setOnClickPendingIntent(R.id.timetableWidgetAccount, accountPickerPendingIntent) - } - } - - private fun getAccountInitials(name: String): String { - val firstLetters = name.split(" ").mapNotNull { it.firstOrNull() } - return firstLetters.joinToString(separator = "").uppercase() - } - - private fun createAccountPickerPendingIntent(context: Context, widgetId: Int) = - PendingIntent.getActivity( - context, - -Int.MAX_VALUE + widgetId, - Intent(context, TimetableWidgetConfigureActivity::class.java).apply { - addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK) - putExtra(EXTRA_APPWIDGET_ID, widgetId) - putExtra(EXTRA_FROM_PROVIDER, true) - }, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE - ) - - private fun getAvatarBackgroundBitmap(context: Context, avatarColor: Long): Bitmap? { - val avatarDrawableResource = R.drawable.background_timetable_widget_avatar - return AppCompatResources.getDrawable(context, avatarDrawableResource)?.let { drawable -> - val screenDensity = context.resources.displayMetrics.density - val avatarSize = (48 * screenDensity).toInt() - DrawableCompat.wrap(drawable).run { - DrawableCompat.setTint(this, avatarColor.toInt()) - toBitmap(avatarSize, avatarSize) + val avatarBitmap = + if (avatarDrawable.intrinsicWidth <= 0 || avatarDrawable.intrinsicHeight <= 0) { + Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + } else { + Bitmap.createBitmap( + avatarDrawable.intrinsicWidth, + avatarDrawable.intrinsicHeight, + Bitmap.Config.ARGB_8888 + ) } - } + + val canvas = Canvas(avatarBitmap) + avatarDrawable.setBounds(0, 0, canvas.width, canvas.height) + avatarDrawable.draw(canvas) + return avatarBitmap } - private fun onWidgetDeleted(intent: Intent) { - val deletedWidgetId = intent.getWidgetId() - deleteWidgetPreferences(deletedWidgetId) - } + private fun getWidgetDefaultDateToLoad(appWidgetId: Int): LocalDate { + val lastLessonEndTimestamp = + sharedPref.getLong(getTodayLastLessonEndDateTimeWidgetKey(appWidgetId), 0) + val lastLessonEndDateTime = + LocalDateTime.ofEpochSecond(lastLessonEndTimestamp, 0, ZoneOffset.UTC) - private fun Intent.getWidgetId(): Int { - return getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID) - } + val todayDate = LocalDate.now() + val isLastLessonEndDateNow = lastLessonEndDateTime.toLocalDate() == todayDate + val isLastLessonEndDateAfterNowTime = LocalDateTime.now() > lastLessonEndDateTime - private fun deleteWidgetPreferences(widgetId: Int) { - with(sharedPref) { - delete(getStudentWidgetKey(widgetId)) - delete(getDateWidgetKey(widgetId)) + return if (isLastLessonEndDateNow && isLastLessonEndDateAfterNowTime) { + todayDate.nextSchoolDay + } else { + todayDate.nextOrSameSchoolDay } } } 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 e16db53c6..962e5b208 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt @@ -25,8 +25,7 @@ open class AppInfo @Inject constructor() { open val systemModel: String get() = Build.MODEL - open val messagesBaseUrl: String = BuildConfig.MESSAGES_BASE_URL - open val schoolsBaseUrl: String = BuildConfig.SCHOOLS_BASE_URL + open val messagesBaseUrl = BuildConfig.MESSAGES_BASE_URL @Suppress("DEPRECATION") open val systemLanguage: String diff --git a/app/src/main/java/io/github/wulkanowy/utils/BaseRemoteConfigHelper.kt b/app/src/main/java/io/github/wulkanowy/utils/BaseRemoteConfigHelper.kt deleted file mode 100644 index 002612a87..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/BaseRemoteConfigHelper.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.wulkanowy.utils - -abstract class BaseRemoteConfigHelper { - - open fun initialize() = Unit - - open val userAgentTemplate: String - get() = RemoteConfigDefaults.USER_AGENT_TEMPLATE.value as String -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt deleted file mode 100644 index d3c9f8006..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.utils - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.os.Parcelable -import java.io.Serializable - -inline fun Bundle.serializable(key: String): T = when { - Build.VERSION.SDK_INT >= 33 -> getSerializable(key, T::class.java)!! - else -> @Suppress("DEPRECATION") getSerializable(key) as T -} - -inline fun Bundle.nullableSerializable(key: String): T? = when { - Build.VERSION.SDK_INT >= 33 -> getSerializable(key, T::class.java) - else -> @Suppress("DEPRECATION") getSerializable(key) as T? -} - -@Suppress("UNCHECKED_CAST") -inline fun Bundle.parcelableArray(key: String): Array? = when { - Build.VERSION.SDK_INT >= 33 -> getParcelableArray(key, T::class.java) - else -> @Suppress("DEPRECATION") getParcelableArray(key) as Array? -} - -inline fun Intent.serializable(key: String): T = when { - Build.VERSION.SDK_INT >= 33 -> getSerializableExtra(key, T::class.java)!! - else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T -} - -inline fun Intent.nullableSerializable(key: String): T? = when { - Build.VERSION.SDK_INT >= 33 -> getSerializableExtra(key, T::class.java) - else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T? -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt index 77f3eb64a..dd91d36d4 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -2,7 +2,6 @@ package io.github.wulkanowy.utils import android.annotation.SuppressLint import android.content.Context -import android.content.res.ColorStateList import android.graphics.* import android.text.TextPaint import android.util.DisplayMetrics.DENSITY_DEFAULT @@ -86,7 +85,3 @@ fun Context.createNameInitialsDrawable( return RoundedBitmapDrawableFactory.create(this.resources, bitmap) .apply { isCircular = true } } - -fun Context.getAttrColorStateList(@AttrRes color: Int): ColorStateList { - return ColorStateList.valueOf(getThemeAttrColor(color)) -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt index a4c2537ac..43cecd400 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt @@ -15,17 +15,16 @@ import java.net.ConnectException import java.net.SocketException import java.net.SocketTimeoutException import java.net.UnknownHostException -import java.security.cert.CertPathValidatorException import java.security.cert.CertificateExpiredException import java.security.cert.CertificateNotYetValidException import javax.net.ssl.SSLHandshakeException fun Resources.getErrorString(error: Throwable): String = when (error) { is UnknownHostException -> R.string.error_no_internet - is ConnectException, is SocketException, is SocketTimeoutException, is InterruptedIOException, + is ConnectException, is StreamResetException -> R.string.error_timeout is NotLoggedInException -> R.string.error_login_failed is PasswordChangeRequiredException -> R.string.error_password_change_required @@ -43,10 +42,10 @@ fun Resources.getErrorString(error: Throwable): String = when (error) { fun Throwable.isShouldBeReported(): Boolean = when (this) { is UnknownHostException, - is ConnectException, is SocketException, is SocketTimeoutException, is InterruptedIOException, + is ConnectException, is StreamResetException, is ServiceUnavailableException, is FeatureDisabledException, @@ -71,6 +70,5 @@ private fun Throwable?.isCausedByCertificateNotValidNow(): Boolean { private fun Throwable?.isCertificateNotValidNow(): Boolean { val isNotYetValid = this is CertificateNotYetValidException val isExpired = this is CertificateExpiredException - val isInvalidPath = this is CertPathValidatorException - return isNotYetValid || isExpired || isInvalidPath + return isNotYetValid || isExpired } 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 61924d4e9..ff65d6376 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt @@ -4,7 +4,6 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.enums.GradeColorTheme -import io.github.wulkanowy.sdk.scrapper.grades.getGradeValueWithModifier import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid fun List.calcAverage(isOptionalArithmeticAverage: Boolean): Double { @@ -21,15 +20,20 @@ fun List.calcAverage(isOptionalArithmeticAverage: Boolean): Double { } fun List.calcFinalAverage(plusModifier: Double, minusModifier: Double) = asSequence() - .mapNotNull { summary -> - val (gradeValue, gradeModifier) = getGradeValueWithModifier(summary.finalGrade) - if (gradeValue == null || gradeModifier == null) return@mapNotNull null - - when { - gradeModifier > 0 -> gradeValue + plusModifier - gradeModifier < 0 -> gradeValue - minusModifier - else -> gradeValue + 0.0 - } + .mapNotNull { + if (it.finalGrade.matches("[0-6][+-]?".toRegex())) { + when { + it.finalGrade.endsWith('+') -> { + it.finalGrade.removeSuffix("+").toDouble() + plusModifier + } + it.finalGrade.endsWith('-') -> { + it.finalGrade.removeSuffix("-").toDouble() - minusModifier + } + else -> { + it.finalGrade.toDouble() + } + } + } else null } .average() .let { if (it.isNaN()) 0.0 else it } diff --git a/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt index 62b85af4d..1ef03f2e6 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt @@ -1,15 +1,11 @@ package io.github.wulkanowy.utils -import android.app.Activity import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent import android.net.Uri -import android.os.Build import android.provider.CalendarContract -import android.provider.Settings import io.github.wulkanowy.BuildConfig -import timber.log.Timber import java.time.LocalDateTime import java.time.ZoneId @@ -90,23 +86,6 @@ fun Context.openDialer(phone: String) { } } -fun Activity.openNotificationSettings() { - val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply { - putExtra("android.provider.extra.APP_PACKAGE", packageName) - } - } else { - Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { - data = Uri.fromParts("package", packageName, null) - } - } - try { - startActivity(intent) - } catch (e: Exception) { - Timber.e(e) - } -} - fun Context.shareText(text: String, subject: String?) { val sendIntent: Intent = Intent().apply { action = Intent.ACTION_SEND 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 76ce66dc5..032e2d28a 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/LifecycleAwareVariable.kt @@ -3,7 +3,6 @@ package io.github.wulkanowy.utils import android.os.Handler import android.os.Looper import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner @@ -30,18 +29,18 @@ class LifecycleAwareVariable : ReadWriteProperty, DefaultL } } -class LifecycleAwareVariableComponent : ReadWriteProperty, +class LifecycleAwareVariableActivity : ReadWriteProperty, DefaultLifecycleObserver { private var _value: T? = null - override fun setValue(thisRef: LifecycleOwner, property: KProperty<*>, value: T) { + override fun setValue(thisRef: AppCompatActivity, property: KProperty<*>, value: T) { thisRef.lifecycle.removeObserver(this) _value = value thisRef.lifecycle.addObserver(this) } - override fun getValue(thisRef: LifecycleOwner, property: KProperty<*>) = _value + override fun getValue(thisRef: AppCompatActivity, property: KProperty<*>) = _value ?: throw IllegalStateException("Trying to call an lifecycle-aware value outside of the view lifecycle, or the value has not been initialized") override fun onDestroy(owner: LifecycleOwner) { @@ -54,8 +53,4 @@ class LifecycleAwareVariableComponent : ReadWriteProperty Fragment.lifecycleAwareVariable() = LifecycleAwareVariable() -@Suppress("unused") -fun DialogFragment.lifecycleAwareVariable() = LifecycleAwareVariableComponent() - -@Suppress("unused") -fun AppCompatActivity.lifecycleAwareVariable() = LifecycleAwareVariableComponent() +fun lifecycleAwareVariable() = LifecycleAwareVariableActivity() diff --git a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt index 00ed2c110..1e9f49a66 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt @@ -7,7 +7,7 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager -import fr.bipi.treessence.common.filters.Filter +import fr.bipi.tressence.common.filters.Filter import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException import timber.log.Timber 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 721297513..c69fec65c 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt @@ -4,7 +4,6 @@ import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.db.SharedPrefProvider -import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.enums.MessageFolder @@ -26,8 +25,8 @@ fun getRefreshKey(name: String, student: Student): String { return "${name}_${student.userLoginId}" } -fun getRefreshKey(name: String, mailbox: Mailbox?, folder: MessageFolder): String { - return "${name}_${mailbox?.globalKey ?: "all"}_${folder.id}" +fun getRefreshKey(name: String, student: Student, folder: MessageFolder): String { + return "${name}_${student.id}_${folder.id}" } class AutoRefreshHelper @Inject constructor( @@ -49,9 +48,4 @@ class AutoRefreshHelper @Inject constructor( fun updateLastRefreshTimestamp(key: String) { sharedPref.putLong(key, Instant.now().toEpochMilli()) } - - fun getLastRefreshTimestamp(key: String): Instant { - val refreshTimestampMilli = sharedPref.getLong(key, 0) - return Instant.ofEpochMilli(refreshTimestampMilli) - } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/RemoteConfigDefaults.kt b/app/src/main/java/io/github/wulkanowy/utils/RemoteConfigDefaults.kt deleted file mode 100644 index 6e8f7ae65..000000000 --- a/app/src/main/java/io/github/wulkanowy/utils/RemoteConfigDefaults.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.wulkanowy.utils - -enum class RemoteConfigDefaults(val value: Any) { - USER_AGENT_TEMPLATE(""), - ; - - val key get() = name.lowercase() -} diff --git a/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt index df99be98b..63a30db8c 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt @@ -11,20 +11,19 @@ fun Sdk.init(student: Student): Sdk { schoolSymbol = student.schoolSymbol studentId = student.studentId classId = student.classId - emptyCookieJarInterceptor = true - if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) { - mobileBaseUrl = student.mobileBaseUrl - } else { + if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { scrapperBaseUrl = student.scrapperBaseUrl - domainSuffix = student.scrapperDomainSuffix loginType = Sdk.ScrapperLoginType.valueOf(student.loginType) } + loginId = student.userLoginId mode = Sdk.Mode.valueOf(student.loginMode) mobileBaseUrl = student.mobileBaseUrl - keyId = student.certificateKey - privatePem = student.privateKey + certKey = student.certificateKey + privateKey = student.privateKey + + emptyCookieJarInterceptor = true Timber.d("Sdk in ${student.loginMode} mode reinitialized") diff --git a/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt index e3b8a3b4c..380d6bf6e 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt @@ -1,27 +1,16 @@ package io.github.wulkanowy.utils import io.github.wulkanowy.data.db.entities.Semester -import java.time.LocalDate import java.time.LocalDate.now -import java.time.Month -fun Semester.isCurrent(now: LocalDate = now()): Boolean { - val shiftedStart = if (start.month == Month.SEPTEMBER) { - start.minusDays(3) - } else start - - val shiftedEnd = if (end.month == Month.AUGUST || end.month == Month.SEPTEMBER) { - end.minusDays(3) - } else end - - return now in shiftedStart..shiftedEnd -} +inline val Semester.isCurrent: Boolean + get() = now() in start..end fun List.getCurrentOrLast(): Semester { if (isEmpty()) throw RuntimeException("Empty semester list") // when there is only one current semester - singleOrNull { it.isCurrent() }?.let { return it } + singleOrNull { it.isCurrent }?.let { return it } // when there is more than one current semester - find one with higher id singleOrNull { semester -> semester.semesterId == maxByOrNull { it.semesterId }?.semesterId }?.let { return it } 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 8043e3659..bddd7df4c 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/StringExtension.kt @@ -1,15 +1,7 @@ package io.github.wulkanowy.utils -import androidx.core.text.parseAsHtml -import org.apache.commons.text.StringEscapeUtils - inline fun String?.ifNullOrBlank(defaultValue: () -> String) = if (isNullOrBlank()) defaultValue() else this fun String.capitalise() = - replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } - -fun String.parseUonetHtml() = this - .let(StringEscapeUtils::unescapeHtml4) - .replace("\n", "
") - .parseAsHtml() + replaceFirstChar { if (it.isLowerCase()) it.titlecase() else it.toString() } \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/utils/StudentExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/StudentExtension.kt index 132a3085e..fdd0610a0 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/StudentExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/StudentExtension.kt @@ -2,4 +2,4 @@ package io.github.wulkanowy.utils import io.github.wulkanowy.data.db.entities.Student -inline val Student.nickOrName get() = nick.ifBlank { studentName } +inline val Student.nickOrName get() = if (nick.isBlank()) studentName else nick diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt index db16a2563..c994ebab6 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 @@ -16,7 +16,6 @@ import android.util.Base64.DEFAULT import android.util.Base64.decode import android.util.Base64.encode import android.util.Base64.encodeToString -import dagger.hilt.android.qualifiers.ApplicationContext import timber.log.Timber import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream @@ -34,124 +33,108 @@ import javax.crypto.CipherInputStream import javax.crypto.CipherOutputStream import javax.crypto.spec.OAEPParameterSpec import javax.crypto.spec.PSource.PSpecified -import javax.inject.Inject -import javax.inject.Singleton import javax.security.auth.x500.X500Principal -@Singleton -class Scrambler @Inject constructor( - @ApplicationContext private val context: Context, -) { - private val keyCharset = Charset.forName("UTF-8") +private const val KEYSTORE_NAME = "AndroidKeyStore" - private val isKeyPairExists: Boolean - get() = keyStore.getKey(KEY_ALIAS, null) != null +private const val KEY_ALIAS = "wulkanowy_password" - private val keyStore: KeyStore - get() = KeyStore.getInstance(KEYSTORE_NAME).apply { load(null) } +private val KEY_CHARSET = Charset.forName("UTF-8") - private val cipher: Cipher - get() { - return if (SDK_INT >= M) Cipher.getInstance( - "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", - "AndroidKeyStoreBCWorkaround" - ) - else Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL") - } +private val isKeyPairExists: Boolean + get() = keyStore.getKey(KEY_ALIAS, null) != null - fun encrypt(plainText: String): String { - if (plainText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") +private val keyStore: KeyStore + get() = KeyStore.getInstance(KEYSTORE_NAME).apply { load(null) } - return try { - if (!isKeyPairExists) generateKeyPair() +private val cipher: Cipher + get() { + return if (SDK_INT >= M) Cipher.getInstance( + "RSA/ECB/OAEPWithSHA-256AndMGF1Padding", + "AndroidKeyStoreBCWorkaround" + ) + else Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL") + } - cipher.let { - if (SDK_INT >= M) { - OAEPParameterSpec("SHA-256", "MGF1", SHA1, PSpecified.DEFAULT).let { spec -> - it.init(ENCRYPT_MODE, keyStore.getCertificate(KEY_ALIAS).publicKey, spec) - } - } else it.init(ENCRYPT_MODE, keyStore.getCertificate(KEY_ALIAS).publicKey) +fun encrypt(plainText: String, context: Context): String { + if (plainText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") - ByteArrayOutputStream().let { output -> - CipherOutputStream(output, it).apply { - write(plainText.toByteArray(keyCharset)) - close() - } - encodeToString(output.toByteArray(), DEFAULT) + return try { + if (!isKeyPairExists) generateKeyPair(context) + + cipher.let { + if (SDK_INT >= M) { + OAEPParameterSpec("SHA-256", "MGF1", SHA1, PSpecified.DEFAULT).let { spec -> + it.init(ENCRYPT_MODE, keyStore.getCertificate(KEY_ALIAS).publicKey, spec) } - } - } catch (exception: Exception) { - Timber.e(exception, "An error occurred while encrypting text") - String(encode(plainText.toByteArray(keyCharset), DEFAULT), keyCharset) - } - } + } else it.init(ENCRYPT_MODE, keyStore.getCertificate(KEY_ALIAS).publicKey) - fun decrypt(cipherText: String): String { - if (cipherText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") - - return try { - if (!isKeyPairExists) throw ScramblerException("KeyPair doesn't exist") - - cipher.let { - if (SDK_INT >= M) { - OAEPParameterSpec("SHA-256", "MGF1", SHA1, PSpecified.DEFAULT).let { spec -> - it.init(DECRYPT_MODE, keyStore.getKey(KEY_ALIAS, null), spec) - } - } else it.init(DECRYPT_MODE, keyStore.getKey(KEY_ALIAS, null)) - - CipherInputStream( - ByteArrayInputStream(decode(cipherText, DEFAULT)), - it - ).let { input -> - val values = ArrayList() - var nextByte: Int - while (run { nextByte = input.read(); nextByte } != -1) { - values.add(nextByte.toByte()) - } - val bytes = ByteArray(values.size) - for (i in bytes.indices) { - bytes[i] = values[i] - } - String(bytes, 0, bytes.size, keyCharset) + ByteArrayOutputStream().let { output -> + CipherOutputStream(output, it).apply { + write(plainText.toByteArray(KEY_CHARSET)) + close() } - } - } catch (e: Exception) { - throw ScramblerException("An error occurred while decrypting text", e) - } - } - - private fun generateKeyPair() { - (if (SDK_INT >= M) { - KeyGenParameterSpec.Builder(KEY_ALIAS, PURPOSE_DECRYPT or PURPOSE_ENCRYPT) - .setDigests(DIGEST_SHA256, DIGEST_SHA512) - .setEncryptionPaddings(ENCRYPTION_PADDING_RSA_OAEP) - .setCertificateSerialNumber(BigInteger.TEN) - .setCertificateSubject(X500Principal("CN=Wulkanowy")) - .build() - } else { - KeyPairGeneratorSpec.Builder(context) - .setAlias(KEY_ALIAS) - .setSubject(X500Principal("CN=Wulkanowy")) - .setSerialNumber(BigInteger.TEN) - .setStartDate(Calendar.getInstance().time) - .setEndDate(Calendar.getInstance().apply { add(YEAR, 99) }.time) - .build() - }).let { - KeyPairGenerator.getInstance("RSA", KEYSTORE_NAME).apply { - initialize(it) - genKeyPair() + encodeToString(output.toByteArray(), DEFAULT) } } - Timber.i("A new KeyPair has been generated") - } - - fun clearKeyPair() { - keyStore.deleteEntry(KEY_ALIAS) - Timber.i("KeyPair has been cleared") - } - - private companion object { - private const val KEYSTORE_NAME = "AndroidKeyStore" - private const val KEY_ALIAS = "wulkanowy_password" + } catch (exception: Exception) { + Timber.e(exception, "An error occurred while encrypting text") + String(encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET) } } + +fun decrypt(cipherText: String): String { + if (cipherText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") + + return try { + if (!isKeyPairExists) throw ScramblerException("KeyPair doesn't exist") + + cipher.let { + if (SDK_INT >= M) { + OAEPParameterSpec("SHA-256", "MGF1", SHA1, PSpecified.DEFAULT).let { spec -> + it.init(DECRYPT_MODE, keyStore.getKey(KEY_ALIAS, null), spec) + } + } else it.init(DECRYPT_MODE, keyStore.getKey(KEY_ALIAS, null)) + + CipherInputStream(ByteArrayInputStream(decode(cipherText, DEFAULT)), it).let { input -> + val values = ArrayList() + var nextByte: Int + while (run { nextByte = input.read(); nextByte } != -1) { + values.add(nextByte.toByte()) + } + val bytes = ByteArray(values.size) + for (i in bytes.indices) { + bytes[i] = values[i] + } + String(bytes, 0, bytes.size, KEY_CHARSET) + } + } + } catch (e: Exception) { + throw ScramblerException("An error occurred while decrypting text", e) + } +} + +private fun generateKeyPair(context: Context) { + (if (SDK_INT >= M) { + KeyGenParameterSpec.Builder(KEY_ALIAS, PURPOSE_DECRYPT or PURPOSE_ENCRYPT) + .setDigests(DIGEST_SHA256, DIGEST_SHA512) + .setEncryptionPaddings(ENCRYPTION_PADDING_RSA_OAEP) + .setCertificateSerialNumber(BigInteger.TEN) + .setCertificateSubject(X500Principal("CN=Wulkanowy")) + .build() + } else { + KeyPairGeneratorSpec.Builder(context) + .setAlias(KEY_ALIAS) + .setSubject(X500Principal("CN=Wulkanowy")) + .setSerialNumber(BigInteger.TEN) + .setStartDate(Calendar.getInstance().time) + .setEndDate(Calendar.getInstance().apply { add(YEAR, 99) }.time) + .build() + }).let { + KeyPairGenerator.getInstance("RSA", KEYSTORE_NAME).apply { + initialize(it) + genKeyPair() + } + } + Timber.i("A new KeyPair has been generated") +} 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 0a2eb68f4..7fe3b7099 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,5 +1,6 @@ -Wersja 2.3.3 +Wersja 1.7.4 -— poprawiliśmy kolejne usterki przy odświeżaniu danych (teraz to powinno działać już dużo lepiej) +- naprawiliśmy kilka błędów w obsłudze nowego modułu wiadomości +- naprawiliśmy wyświetlanie napisu "Brak ocen", jesli uczeń nie zdobył w danym semestrze jeszcze żadnych ocen Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases diff --git a/app/src/main/res/drawable-night/background_header_note.xml b/app/src/main/res/drawable-night/background_header_note.xml new file mode 100644 index 000000000..6b594e7c6 --- /dev/null +++ b/app/src/main/res/drawable-night/background_header_note.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/background_grade_details_rounded.xml b/app/src/main/res/drawable/background_grade_details_rounded.xml deleted file mode 100644 index e24088a0c..000000000 --- a/app/src/main/res/drawable/background_grade_details_rounded.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/background_grade_details_weight_rounded.xml b/app/src/main/res/drawable/background_grade_details_weight_rounded.xml deleted file mode 100644 index 4b2109128..000000000 --- a/app/src/main/res/drawable/background_grade_details_weight_rounded.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/background_header_note.xml b/app/src/main/res/drawable/background_header_note.xml index 8cf84a1cd..c21e55c6b 100644 --- a/app/src/main/res/drawable/background_header_note.xml +++ b/app/src/main/res/drawable/background_header_note.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/drawable/background_luckynumber_widget_button.xml b/app/src/main/res/drawable/background_luckynumber_widget.xml similarity index 65% rename from app/src/main/res/drawable/background_luckynumber_widget_button.xml rename to app/src/main/res/drawable/background_luckynumber_widget.xml index 66b1685f6..367c55275 100644 --- a/app/src/main/res/drawable/background_luckynumber_widget_button.xml +++ b/app/src/main/res/drawable/background_luckynumber_widget.xml @@ -1,6 +1,6 @@ - - + + diff --git a/app/src/main/res/drawable/background_luckynumber_widget_dark.xml b/app/src/main/res/drawable/background_luckynumber_widget_dark.xml new file mode 100644 index 000000000..cb094b57e --- /dev/null +++ b/app/src/main/res/drawable/background_luckynumber_widget_dark.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/background_material_alert_dialog.xml b/app/src/main/res/drawable/background_material_alert_dialog.xml deleted file mode 100644 index 5ab8a3506..000000000 --- a/app/src/main/res/drawable/background_material_alert_dialog.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/background_timetable_widget_avatar.xml b/app/src/main/res/drawable/background_timetable_widget_avatar.xml deleted file mode 100644 index 48298d675..000000000 --- a/app/src/main/res/drawable/background_timetable_widget_avatar.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/background_widget_header_timetable.xml similarity index 57% rename from app/src/main/res/drawable/ic_circle.xml rename to app/src/main/res/drawable/background_widget_header_timetable.xml index d2932fe62..98eec700d 100644 --- a/app/src/main/res/drawable/ic_circle.xml +++ b/app/src/main/res/drawable/background_widget_header_timetable.xml @@ -1,10 +1,7 @@ - - + - - - + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_widget_header_timetable_dark.xml b/app/src/main/res/drawable/background_widget_header_timetable_dark.xml new file mode 100644 index 000000000..616a91279 --- /dev/null +++ b/app/src/main/res/drawable/background_widget_header_timetable_dark.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_widget_item_timetable.xml b/app/src/main/res/drawable/background_widget_item_timetable.xml index 510c70c0c..08854fba2 100644 --- a/app/src/main/res/drawable/background_widget_item_timetable.xml +++ b/app/src/main/res/drawable/background_widget_item_timetable.xml @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_grade_small_rounded.xml b/app/src/main/res/drawable/background_widget_item_timetable_dark.xml similarity index 51% rename from app/src/main/res/drawable/background_grade_small_rounded.xml rename to app/src/main/res/drawable/background_widget_item_timetable_dark.xml index dd50417f7..e432a648c 100644 --- a/app/src/main/res/drawable/background_grade_small_rounded.xml +++ b/app/src/main/res/drawable/background_widget_item_timetable_dark.xml @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_widget_timetable.xml b/app/src/main/res/drawable/background_widget_timetable.xml index b589ad29d..2267587d9 100644 --- a/app/src/main/res/drawable/background_widget_timetable.xml +++ b/app/src/main/res/drawable/background_widget_timetable.xml @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/background_grade_rounded.xml b/app/src/main/res/drawable/background_widget_timetable_dark.xml similarity index 74% rename from app/src/main/res/drawable/background_grade_rounded.xml rename to app/src/main/res/drawable/background_widget_timetable_dark.xml index 52c10c2f4..6fe7d0ab2 100644 --- a/app/src/main/res/drawable/background_grade_rounded.xml +++ b/app/src/main/res/drawable/background_widget_timetable_dark.xml @@ -1,5 +1,5 @@ - + - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_auth_success.xml b/app/src/main/res/drawable/ic_auth_success.xml deleted file mode 100644 index 015553c02..000000000 --- a/app/src/main/res/drawable/ic_auth_success.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_chevron_left.xml b/app/src/main/res/drawable/ic_chevron_left.xml index 4250fae47..ee3ff4be8 100644 --- a/app/src/main/res/drawable/ic_chevron_left.xml +++ b/app/src/main/res/drawable/ic_chevron_left.xml @@ -1,10 +1,5 @@ - - + + diff --git a/app/src/main/res/drawable/ic_chevron_right.xml b/app/src/main/res/drawable/ic_chevron_right.xml index de5037cf0..a6d734973 100644 --- a/app/src/main/res/drawable/ic_chevron_right.xml +++ b/app/src/main/res/drawable/ic_chevron_right.xml @@ -1,10 +1,5 @@ - - + + diff --git a/app/src/main/res/drawable/ic_error_filled.xml b/app/src/main/res/drawable/ic_error_filled.xml deleted file mode 100644 index 61b575dc6..000000000 --- a/app/src/main/res/drawable/ic_error_filled.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_history.xml b/app/src/main/res/drawable/ic_history.xml deleted file mode 100644 index f20e2094b..000000000 --- a/app/src/main/res/drawable/ic_history.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground_dev_mono.xml b/app/src/main/res/drawable/ic_launcher_foreground_dev_mono.xml deleted file mode 100644 index b1b01a0b6..000000000 --- a/app/src/main/res/drawable/ic_launcher_foreground_dev_mono.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground_mono.xml b/app/src/main/res/drawable/ic_launcher_foreground_mono.xml deleted file mode 100644 index e2e747316..000000000 --- a/app/src/main/res/drawable/ic_launcher_foreground_mono.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_login.xml b/app/src/main/res/drawable/ic_login.xml deleted file mode 100644 index 57aef3dca..000000000 --- a/app/src/main/res/drawable/ic_login.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_menu_order_drag.xml b/app/src/main/res/drawable/ic_menu_order_drag.xml deleted file mode 100644 index 623c67d2e..000000000 --- a/app/src/main/res/drawable/ic_menu_order_drag.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_scale_balance.xml b/app/src/main/res/drawable/ic_scale_balance.xml deleted file mode 100644 index c65467a6c..000000000 --- a/app/src/main/res/drawable/ic_scale_balance.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_timetable_widget_swap.xml b/app/src/main/res/drawable/ic_timetable_widget_swap.xml deleted file mode 100644 index 09c50b4f1..000000000 --- a/app/src/main/res/drawable/ic_timetable_widget_swap.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_widget_chevron.png b/app/src/main/res/drawable/ic_widget_chevron.png new file mode 100644 index 000000000..34345521a Binary files /dev/null and b/app/src/main/res/drawable/ic_widget_chevron.png differ diff --git a/app/src/main/res/drawable/ic_widget_chevron.xml b/app/src/main/res/drawable/ic_widget_chevron.xml deleted file mode 100644 index 871aca9bf..000000000 --- a/app/src/main/res/drawable/ic_widget_chevron.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/img_luckynumber_widget_preview.png b/app/src/main/res/drawable/img_luckynumber_widget_preview.png index 267018550..539b0a598 100644 Binary files a/app/src/main/res/drawable/img_luckynumber_widget_preview.png and b/app/src/main/res/drawable/img_luckynumber_widget_preview.png differ diff --git a/app/src/main/res/drawable/img_timetable_widget_preview.png b/app/src/main/res/drawable/img_timetable_widget_preview.png old mode 100644 new mode 100755 index 5c1752cb5..849430187 Binary files a/app/src/main/res/drawable/img_timetable_widget_preview.png and b/app/src/main/res/drawable/img_timetable_widget_preview.png differ diff --git a/app/src/main/res/drawable/shape_badge.xml b/app/src/main/res/drawable/shape_badge.xml deleted file mode 100644 index 3153f592d..000000000 --- a/app/src/main/res/drawable/shape_badge.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout-v31/widget_timetable_preview.xml b/app/src/main/res/layout-v31/widget_timetable_preview.xml deleted file mode 100644 index 2c47836cc..000000000 --- a/app/src/main/res/layout-v31/widget_timetable_preview.xml +++ /dev/null @@ -1,347 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index d14de50a1..11844e244 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,31 +1,24 @@ - - - - + app:contentInsetStartWithNavigation="0dp" + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toBottomOf="@id/main_toolbar" /> - - - - + android:elevation="4dp" + app:layout_constraintBottom_toTopOf="@id/sendMessageScroll" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/sendMessageToolbar"> + android:layout_height="match_parent"> - - + app:layout_constraintTop_toBottomOf="@id/sendMessageToolbar" /> diff --git a/app/src/main/res/layout/dialog_account_edit.xml b/app/src/main/res/layout/dialog_account_edit.xml index 2ab4ccc6a..9f617e440 100644 --- a/app/src/main/res/layout/dialog_account_edit.xml +++ b/app/src/main/res/layout/dialog_account_edit.xml @@ -1,14 +1,19 @@ - + + android:layout_height="match_parent" + android:fillViewport="true" + android:minWidth="300dp" + android:paddingStart="8dp" + android:paddingEnd="8dp"> - + android:minWidth="300dp" + android:orientation="vertical"> + android:textSize="21sp" + android:textStyle="bold" /> - + android:text="@string/additional_lessons_repeat" /> + android:hint="@string/all_subject"> - + - - + + + + + diff --git a/app/src/main/res/layout/dialog_ads_consent.xml b/app/src/main/res/layout/dialog_ads_consent.xml new file mode 100644 index 000000000..816074783 --- /dev/null +++ b/app/src/main/res/layout/dialog_ads_consent.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_attendance.xml b/app/src/main/res/layout/dialog_attendance.xml index d832490a7..08ed9d4a3 100644 --- a/app/src/main/res/layout/dialog_attendance.xml +++ b/app/src/main/res/layout/dialog_attendance.xml @@ -5,21 +5,28 @@ android:layout_height="match_parent"> + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_conference.xml b/app/src/main/res/layout/dialog_conference.xml index 72837b819..d08edf4f7 100644 --- a/app/src/main/res/layout/dialog_conference.xml +++ b/app/src/main/res/layout/dialog_conference.xml @@ -6,20 +6,28 @@ android:layout_height="match_parent"> + + + android:layout_height="match_parent" + tools:context=".ui.modules.exam.ExamDialog"> + + + app:layout_constraintStart_toStartOf="parent" /> + android:layout_height="wrap_content"> + android:hint="@string/attendance_excuse_dialog_reason" /> diff --git a/app/src/main/res/layout/dialog_grade.xml b/app/src/main/res/layout/dialog_grade.xml index f47f61088..9c52c1d0b 100644 --- a/app/src/main/res/layout/dialog_grade.xml +++ b/app/src/main/res/layout/dialog_grade.xml @@ -21,7 +21,7 @@ - - - - - - + android:background="@color/grade_black" + android:gravity="center" + android:maxLines="2" + android:minHeight="32dp" + android:textColor="@android:color/white" + android:textIsSelectable="true" + android:textSize="14sp" + tools:text="Waga: 1.00" /> + android:textIsSelectable="true" + android:textSize="21sp" + android:textStyle="bold" /> + android:layout_marginTop="8dp" + android:textIsSelectable="true" + android:textSize="16sp" /> + android:textColor="?android:textColorSecondary" + android:textSize="12sp" /> + android:textIsSelectable="true" + android:textSize="16sp" /> + android:textColor="?android:textColorSecondary" + android:textSize="12sp" /> + android:textIsSelectable="true" + android:textSize="16sp" /> + android:textColor="?android:textColorSecondary" + android:textSize="12sp" /> + android:textIsSelectable="true" + android:textSize="16sp" /> + android:textColor="?android:textColorSecondary" + android:textSize="12sp" /> + android:textIsSelectable="true" + android:textSize="16sp" /> + + diff --git a/app/src/main/res/layout/dialog_homework.xml b/app/src/main/res/layout/dialog_homework.xml index 8c6cf0a76..341cec544 100644 --- a/app/src/main/res/layout/dialog_homework.xml +++ b/app/src/main/res/layout/dialog_homework.xml @@ -1,56 +1,71 @@ - + android:layout_height="match_parent" + android:minWidth="300dp" + android:orientation="vertical"> + android:background="@drawable/ic_all_divider" /> - + - - + + + + + diff --git a/app/src/main/res/layout/dialog_homework_add.xml b/app/src/main/res/layout/dialog_homework_add.xml index e0ff5b749..524f0db0d 100644 --- a/app/src/main/res/layout/dialog_homework_add.xml +++ b/app/src/main/res/layout/dialog_homework_add.xml @@ -2,35 +2,37 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:fillViewport="true" + android:minWidth="300dp" + android:paddingStart="8dp" + android:paddingEnd="8dp"> - + android:minWidth="300dp" + android:orientation="vertical"> + android:textSize="21sp" + android:textStyle="bold" /> + android:hint="@string/all_subject"> + android:hint="@string/all_teacher"> + android:hint="@string/all_content"> - + - - + + + + + diff --git a/app/src/main/res/layout/dialog_lesson_completed.xml b/app/src/main/res/layout/dialog_lesson_completed.xml index 3a1d3fd00..500cdb6f3 100644 --- a/app/src/main/res/layout/dialog_lesson_completed.xml +++ b/app/src/main/res/layout/dialog_lesson_completed.xml @@ -1,5 +1,4 @@ - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_mailbox_chooser.xml b/app/src/main/res/layout/dialog_mailbox_chooser.xml deleted file mode 100644 index 5c93acb59..000000000 --- a/app/src/main/res/layout/dialog_mailbox_chooser.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/layout/dialog_mobile_device.xml b/app/src/main/res/layout/dialog_mobile_device.xml index 9b81737fb..043afb2ad 100644 --- a/app/src/main/res/layout/dialog_mobile_device.xml +++ b/app/src/main/res/layout/dialog_mobile_device.xml @@ -7,10 +7,18 @@ tools:context=".ui.modules.mobiledevice.token.MobileDeviceTokenDialog"> + + - + + - + + + + + app:layout_constraintTop_toBottomOf="@id/timetableDialogLessonValue" /> + app:layout_constraintTop_toBottomOf="@id/timetableDialogTeacherValue" /> + app:layout_constraintTop_toBottomOf="@id/timetableDialogRoomValue" /> @@ -105,7 +106,7 @@ + tools:ignore="UnusedAttribute"> + android:paddingBottom="6dp" /> - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_exam.xml b/app/src/main/res/layout/fragment_exam.xml index 8dba5361b..0c62aab5f 100644 --- a/app/src/main/res/layout/fragment_exam.xml +++ b/app/src/main/res/layout/fragment_exam.xml @@ -91,7 +91,7 @@ diff --git a/app/src/main/res/layout/fragment_login_advanced.xml b/app/src/main/res/layout/fragment_login_advanced.xml index 37551decb..c7acaa70b 100644 --- a/app/src/main/res/layout/fragment_login_advanced.xml +++ b/app/src/main/res/layout/fragment_login_advanced.xml @@ -97,7 +97,7 @@ @@ -185,34 +185,9 @@ tools:ignore="Deprecated,LabelFor" /> - - - - - + app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout"> - - + app:layout_constraintTop_toBottomOf="@+id/loginFormUsernameLayout" + app:passwordToggleEnabled="true"> - - - - - + app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout" /> + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + tools:itemCount="6" + tools:listitem="@layout/item_login_student_select" /> - - diff --git a/app/src/main/res/layout/fragment_login_symbol.xml b/app/src/main/res/layout/fragment_login_symbol.xml index 37f4c731a..f1c9be4a0 100644 --- a/app/src/main/res/layout/fragment_login_symbol.xml +++ b/app/src/main/res/layout/fragment_login_symbol.xml @@ -66,7 +66,7 @@ + app:layout_constraintTop_toBottomOf="@+id/loginSymbolHelper"> - - - - diff --git a/app/src/main/res/layout/fragment_message.xml b/app/src/main/res/layout/fragment_message.xml index b3dac0dd1..5269d95e8 100644 --- a/app/src/main/res/layout/fragment_message.xml +++ b/app/src/main/res/layout/fragment_message.xml @@ -29,7 +29,6 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_notifications_center.xml b/app/src/main/res/layout/fragment_notifications_center.xml index ba2f0ff49..f59ce33c9 100644 --- a/app/src/main/res/layout/fragment_notifications_center.xml +++ b/app/src/main/res/layout/fragment_notifications_center.xml @@ -89,7 +89,7 @@ + tools:ignore="UnusedAttribute"> + android:layout_height="match_parent" + android:layout_marginBottom="50dp"> @@ -173,4 +173,4 @@ app:srcCompat="@drawable/ic_chevron_right" app:tint="?colorPrimary" /> - + diff --git a/app/src/main/res/layout/fragment_timetable_completed.xml b/app/src/main/res/layout/fragment_timetable_completed.xml index 8d647ff61..e089275d9 100644 --- a/app/src/main/res/layout/fragment_timetable_completed.xml +++ b/app/src/main/res/layout/fragment_timetable_completed.xml @@ -93,7 +93,7 @@ + android:layout_marginVertical="6dp" + app:cardElevation="4dp"> + tools:text="Szkoła Wulkanowego " /> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_admin_message.xml b/app/src/main/res/layout/item_dashboard_admin_message.xml index e12241df5..67836561b 100644 --- a/app/src/main/res/layout/item_dashboard_admin_message.xml +++ b/app/src/main/res/layout/item_dashboard_admin_message.xml @@ -5,7 +5,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="12dp" - android:layout_marginVertical="6dp"> + android:layout_marginVertical="6dp" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_announcements.xml b/app/src/main/res/layout/item_dashboard_announcements.xml index b9ddb7575..19f720884 100644 --- a/app/src/main/res/layout/item_dashboard_announcements.xml +++ b/app/src/main/res/layout/item_dashboard_announcements.xml @@ -8,7 +8,8 @@ android:layout_marginVertical="6dp" android:clickable="true" android:focusable="true" - android:foreground="?selectableItemBackground"> + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_conferences.xml b/app/src/main/res/layout/item_dashboard_conferences.xml index b02b8e18f..02d3edfc8 100644 --- a/app/src/main/res/layout/item_dashboard_conferences.xml +++ b/app/src/main/res/layout/item_dashboard_conferences.xml @@ -8,7 +8,8 @@ android:layout_marginVertical="6dp" android:clickable="true" android:focusable="true" - android:foreground="?selectableItemBackground"> + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_exams.xml b/app/src/main/res/layout/item_dashboard_exams.xml index 84302403c..9cc98d790 100644 --- a/app/src/main/res/layout/item_dashboard_exams.xml +++ b/app/src/main/res/layout/item_dashboard_exams.xml @@ -8,7 +8,8 @@ android:layout_marginVertical="6dp" android:clickable="true" android:focusable="true" - android:foreground="?selectableItemBackground"> + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_grades.xml b/app/src/main/res/layout/item_dashboard_grades.xml index 345d8a5e4..5cc9ce308 100644 --- a/app/src/main/res/layout/item_dashboard_grades.xml +++ b/app/src/main/res/layout/item_dashboard_grades.xml @@ -6,7 +6,8 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="12dp" android:layout_marginVertical="6dp" - android:foreground="?selectableItemBackground"> + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_homework.xml b/app/src/main/res/layout/item_dashboard_homework.xml index b36afc570..975d66efd 100644 --- a/app/src/main/res/layout/item_dashboard_homework.xml +++ b/app/src/main/res/layout/item_dashboard_homework.xml @@ -8,7 +8,8 @@ android:layout_marginVertical="6dp" android:clickable="true" android:focusable="true" - android:foreground="?selectableItemBackground"> + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_dashboard_horizontal_group.xml b/app/src/main/res/layout/item_dashboard_horizontal_group.xml index 1c9246a19..1d43d5115 100644 --- a/app/src/main/res/layout/item_dashboard_horizontal_group.xml +++ b/app/src/main/res/layout/item_dashboard_horizontal_group.xml @@ -13,6 +13,7 @@ android:layout_width="0dp" android:layout_height="44dp" android:layout_marginVertical="4dp" + app:cardElevation="4dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/dashboard_horizontal_group_item_message_container" app:layout_constraintHorizontal_chainStyle="spread_inside" @@ -36,25 +37,9 @@ app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_goneMarginEnd="16dp" app:tint="?colorOnSurface" tools:ignore="ContentDescription" /> - - - - + tools:text="16" /> @@ -152,6 +121,7 @@ android:layout_width="0dp" android:layout_height="44dp" android:layout_marginVertical="4dp" + app:cardElevation="4dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/dashboard_horizontal_group_item_message_container" @@ -175,25 +145,9 @@ app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_goneMarginEnd="16dp" app:tint="?colorOnSurface" tools:ignore="ContentDescription" /> - - - + \ 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 a40f17f22..9156c1a2f 100644 --- a/app/src/main/res/layout/item_dashboard_lessons.xml +++ b/app/src/main/res/layout/item_dashboard_lessons.xml @@ -5,7 +5,11 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="12dp" - android:layout_marginVertical="6dp"> + android:layout_marginVertical="6dp" + android:clickable="true" + android:focusable="true" + android:foreground="?selectableItemBackground" + app:cardElevation="4dp"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/item_grade_details.xml b/app/src/main/res/layout/item_grade_details.xml index 6849e929f..2f3bd2de5 100644 --- a/app/src/main/res/layout/item_grade_details.xml +++ b/app/src/main/res/layout/item_grade_details.xml @@ -20,12 +20,11 @@ android:id="@+id/gradeItemValue" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="@drawable/background_grade_rounded" - android:backgroundTint="@color/grade_material_default" + android:background="@color/grade_material_default" android:gravity="center" android:maxLength="5" android:minWidth="45dp" - android:minHeight="45dp" + android:minHeight="40dp" android:textColor="@android:color/white" android:textSize="16sp" app:layout_constraintBottom_toBottomOf="@+id/gradeDetailsContainer" diff --git a/app/src/main/res/layout/item_grade_statistics_header.xml b/app/src/main/res/layout/item_grade_statistics_header.xml index cc35f6066..92f522ba4 100644 --- a/app/src/main/res/layout/item_grade_statistics_header.xml +++ b/app/src/main/res/layout/item_grade_statistics_header.xml @@ -1,10 +1,8 @@ + android:layout_height="wrap_content"> - + android:paddingEnd="16dp"> -