diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..d744bdd14 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,142 @@ +name: Test and deploy + +on: + push: + branches: [ develop ] + tags: [ '*' ] + pull_request: + branches: [ develop ] + + workflow_dispatch: + +jobs: + build: + name: Pre-build + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: fkirc/skip-duplicate-actions@master + - uses: actions/checkout@v2 + - uses: gradle/wrapper-validation-action@v1 + - uses: actions/setup-java@v1 + with: + java-version: 11 + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }} + - name: Build + run: ./gradlew --build-cache compileFdroidDebugUnitTestKotlin preFdroidDebugAndroidTestBuild dexBuilderFdroidDebugAndroidTest packageFdroidDebug packageFdroidDebugAndroidTest + - name: Prepare build cache + run: tar -cf prebuild.tar .build-cache .gradle app/build + - uses: actions/upload-artifact@v2 + with: + name: prebuild.tar + path: prebuild.tar + + unit-tests: + name: Unit tests + runs-on: ubuntu-latest + timeout-minutes: 10 + needs: [ build ] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: 11 + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }} + - uses: actions/download-artifact@v2 + with: + name: prebuild.tar + - name: Extract build cache + run: tar -xf prebuild.tar + - name: Unit tests + run: | + ./gradlew --build-cache -Pcoverage testFdroidDebugUnitTest --stacktrace + ./gradlew --build-cache -Pcoverage jacocoTestReport --stacktrace + - uses: codecov/codecov-action@v1 + with: + flags: unit + + instrumentation-tests: + name: Instrumentation tests + runs-on: macOS-latest + timeout-minutes: 15 + needs: [ build ] + strategy: + fail-fast: true + matrix: + api-level: [21, 29] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: 11 + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }} + - uses: actions/download-artifact@v2 + with: + name: prebuild.tar + - name: Extract build cache + run: tar -xf prebuild.tar + - name: Instrumentation tests + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + arch: x86 + script: | + ./gradlew --build-cache -Pcoverage connectedFdroidDebugAndroidTest --stacktrace + ./gradlew --build-cache -Pcoverage jacocoTestReport --stacktrace + - uses: codecov/codecov-action@v1 + with: + flags: instrumented,api-${{ matrix.api-level }} + + deploy-google-play: + name: Deploy to google play + runs-on: ubuntu-latest + timeout-minutes: 10 + environment: google-play + needs: [ build, unit-tests, instrumentation-tests ] + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: 11 + - uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }} + - uses: actions/download-artifact@v2 + with: + name: prebuild.tar + - name: Extract build cache + run: tar -xf prebuild.tar + - name: Decrypt keys + env: + ENCRYPT_KEY: ${{ secrets.ENCRYPT_KEY }} + SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }} + run: | + gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg + gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg + gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg + - name: Upload apk to google play + env: + PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }} + PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }} + PLAY_SERVICE_ACCOUNT_EMAIL: ${{ secrets.PLAY_SERVICE_ACCOUNT_EMAIL }} + PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }} + run: ./gradlew publishPlayRelease -PenableFirebase --stacktrace; diff --git a/.gitignore b/.gitignore index d3fb6e4e9..5d3321e3f 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ out/ # Gradle files .gradle/ build/ +.build-cache # Local configuration file (sdk path, etc) local.properties @@ -113,3 +114,6 @@ Thumbs.db !/gradle/wrapper/gradle-wrapper.jar .idea/jarRepositories.xml + + +app/src/release/agconnect-services.json diff --git a/.travis.yml b/.travis.yml index 24280c5f8..04db3a616 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ jdk: oraclejdk8 env: global: - - ANDROID_API_LEVEL=29 - - ANDROID_BUILD_TOOLS_VERSION=29.0.3 + - ANDROID_API_LEVEL=30 + - ANDROID_BUILD_TOOLS_VERSION=30.0.2 cache: directories: @@ -14,7 +14,7 @@ cache: branches: only: - develop - - 0.21.2 + - 0.24.0 android: licenses: @@ -28,22 +28,26 @@ android: - build-tools-$ANDROID_BUILD_TOOLS_VERSION # The SDK version used to compile your project - android-$ANDROID_API_LEVEL - # Additional components + # Additional components - extra-google-google_play_services - extra-google-m2repository - extra-android-m2repository - addon-google_apis-google-$ANDROID_API_LEVEL - # Android emulator + # Android emulator - android-22 - sys-img-armeabi-v7a-android-22 +before_install: + - yes | sdkmanager "platforms;android-30" + - yes | sdkmanager "build-tools;30.0.2" + before_script: - # Launch emulator before the execution - - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a - - emulator -avd test -no-audio -no-window & - - android-wait-for-emulator - - adb shell input keyevent 82 & - - "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash" + # Launch emulator before the execution + - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a + - emulator -avd test -no-audio -no-window & + - android-wait-for-emulator + - adb shell input keyevent 82 & + - "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash" script: - ./gradlew dependencies --stacktrace --daemon @@ -54,6 +58,7 @@ script: - | if [ $TRAVIS_TAG ]; then gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg; + gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/agconnect-services.json.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg; ./gradlew publishPlayRelease -PenableFirebase --stacktrace; diff --git a/README.en.md b/README.en.md index 28cce1c34..accc36082 100644 --- a/README.en.md +++ b/README.en.md @@ -1,7 +1,8 @@ [Polska wersja README](README.md) # Wulkanowy -[![Travis](https://img.shields.io/travis/com/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://travis-ci.com/wulkanowy/wulkanowy) + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Test%20and%20deploy/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/) @@ -32,14 +33,17 @@ Unofficial android VULCAN UONET+ register client for both students and their par ## Download -You can download the current beta version from the Google Play or the F-Droid store +You can download the current beta version from the Google Play, F-Droid or Huawei AppGallery store [Get it on Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) + alt="Get it on Google Play" + height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy) [Get it on F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) +[Explore it on AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) You can also download a [development version](https://wulkanowy.github.io/#download) that includes new features being prepared for the next release @@ -47,7 +51,8 @@ You can also download a [development version](https://wulkanowy.github.io/#downl * [Wulkanowy SDK](https://github.com/wulkanowy/sdk) -* [Dagger 2](https://github.com/google/dagger) +* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) +* [Hilt](https://dagger.dev/hilt/) * [Room](https://developer.android.com/topic/libraries/architecture/room) * [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) diff --git a/README.md b/README.md index 02e1900c8..ca3b55904 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ [English version of README](README.en.md) # Wulkanowy -[![Travis](https://img.shields.io/travis/com/wulkanowy/wulkanowy/master.svg?style=flat-square)](https://travis-ci.com/wulkanowy/wulkanowy) + +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/wulkanowy/wulkanowy/Test%20and%20deploy/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/) @@ -32,14 +33,17 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica ## Pobierz -Aktualną wersję beta możesz pobrać ze sklepu Google Play lub F-Droid +Aktualną wersję beta możesz pobrać ze sklepu Google Play, F-Droid lub Huawei AppGallery [Pobierz z Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) + alt="Pobierz z Google Play" + height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy) [Pobierz z F-Droid](https://f-droid.org/packages/io.github.wulkanowy/) +[Odkrywaj w AppGallery](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=) Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#download), która zawiera nowe funkcje przygotowywane do następnego wydania @@ -47,8 +51,9 @@ Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#downloa ## Zbudowana za pomocą -* [Wulkanowy SDK](https://github.com/wulkanowy/SDK) -* [Dagger 2](https://github.com/google/dagger) +* [Wulkanowy SDK](https://github.com/wulkanowy/sdk) +* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) +* [Hilt](https://dagger.dev/hilt/) * [Room](https://developer.android.com/topic/libraries/architecture/room) * [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) diff --git a/app/build.gradle b/app/build.gradle index 566094662..efb4fe190 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,16 +10,16 @@ apply from: 'sonarqube.gradle' apply from: 'hooks.gradle' android { - compileSdkVersion 29 - buildToolsVersion '29.0.3' + compileSdkVersion 30 + buildToolsVersion '30.0.2' defaultConfig { applicationId "io.github.wulkanowy" testApplicationId "io.github.tests.wulkanowy" minSdkVersion 17 - targetSdkVersion 29 - versionCode 72 - versionName "0.21.2" + targetSdkVersion 30 + versionCode 79 + versionName "0.24.1" multiDexEnabled true resValue "string", "app_name", "Wulkanowy" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -69,12 +69,26 @@ android { flavorDimensions "platform" productFlavors { + hms { + dimension "platform" + minSdkVersion 19 + manifestPlaceholders = [ + install_channel: "AppGallery" + ] + } + play { dimension "platform" + manifestPlaceholders = [ + install_channel: "Google Play" + ] } fdroid { dimension "platform" + manifestPlaceholders = [ + install_channel: "F-Droid" + ] } } @@ -112,29 +126,26 @@ play { serviceAccountCredentials = file('key.p12') defaultToAppBundles = false track = 'alpha' + updatePriority = 3 } ext { work_manager = "2.4.0" - room = "2.2.5" - chucker = "3.2.0" - mockk = "1.10.0" - moshi = "1.9.3" -} - -configurations.all { - resolutionStrategy.force "androidx.constraintlayout:constraintlayout:1.1.3" + room = "2.2.6" + chucker = "3.4.0" + mockk = "1.10.5" + moshi = "1.11.0" } dependencies { - implementation "io.github.wulkanowy:sdk:0.21.2" + implementation "io.github.wulkanowy:sdk:0.24.1" - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2' - implementation "androidx.core:core-ktx:1.3.1" + implementation "androidx.core:core-ktx:1.3.2" implementation "androidx.activity:activity-ktx:1.1.0" implementation "androidx.appcompat:appcompat:1.2.0" implementation "androidx.appcompat:appcompat-resources:1.2.0" @@ -146,7 +157,7 @@ dependencies { implementation "androidx.recyclerview:recyclerview:1.1.0" implementation "androidx.viewpager:viewpager:1.0.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" - implementation "androidx.constraintlayout:constraintlayout:2.0.1" + implementation "androidx.constraintlayout:constraintlayout:2.0.4" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" implementation "com.google.android.material:material:1.2.1" implementation "com.github.wulkanowy:material-chips-input:2.1.1" @@ -154,7 +165,7 @@ dependencies { implementation "me.zhanghai.android.materialprogressbar:library:1.6.1" implementation "androidx.work:work-runtime-ktx:$work_manager" - implementation "androidx.work:work-gcm:$work_manager" + playImplementation "androidx.work:work-gcm:$work_manager" implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0" @@ -179,25 +190,30 @@ dependencies { implementation "fr.bipi.treessence:treessence:0.3.2" implementation "com.mikepenz:aboutlibraries-core:$about_libraries" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' - implementation "io.coil-kt:coil:1.0.0-rc2" + implementation "io.coil-kt:coil:1.1.1" implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation 'me.xdrop:fuzzywuzzy:1.3.1' - playImplementation 'com.google.firebase:firebase-analytics:17.5.0' - playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.1' - playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.1" - playImplementation 'com.google.firebase:firebase-messaging:20.2.4' - playImplementation 'com.google.firebase:firebase-crashlytics:17.2.1' + playImplementation platform('com.google.firebase:firebase-bom:26.3.0') + playImplementation 'com.google.firebase:firebase-analytics-ktx' + playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx' + playImplementation "com.google.firebase:firebase-inappmessaging-ktx" + playImplementation 'com.google.firebase:firebase-messaging:' + playImplementation 'com.google.firebase:firebase-crashlytics:' + playImplementation 'com.google.android.play:core-ktx:1.8.1' playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' + hmsImplementation 'com.huawei.hms:hianalytics:5.1.0.301' + hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.4.2.301' + releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker" debugImplementation "com.amitshekhar.android:debug-db:1.0.6" - testImplementation "junit:junit:4.13" + testImplementation "junit:junit:4.13.1" testImplementation "io.mockk:mockk:$mockk" - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.9' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2' androidTestImplementation "androidx.test:core:1.3.0" androidTestImplementation "androidx.test:runner:1.3.0" @@ -208,3 +224,4 @@ dependencies { } apply plugin: 'com.google.gms.google-services' +apply plugin: 'com.huawei.agconnect' diff --git a/app/jacoco.gradle b/app/jacoco.gradle index a5cf84e63..94469fbc0 100644 --- a/app/jacoco.gradle +++ b/app/jacoco.gradle @@ -7,6 +7,7 @@ jacoco { tasks.withType(Test) { jacoco.includeNoLocationClasses = true + jacoco.excludes = ['jdk.internal.*'] } task jacocoTestReport(type: JacocoReport) { diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json new file mode 100644 index 000000000..c7c4c0331 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json @@ -0,0 +1,1842 @@ +{ + "formatVersion": 1, + "database": { + "version": 28, + "identityHash": "3a449a55ea73fbfbb7973f1f3f834e10", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `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)", + "fields": [ + { + "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 + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `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)", + "fields": [ + { + "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 + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `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)", + "fields": [ + { + "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 + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3a449a55ea73fbfbb7973f1f3f834e10')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json new file mode 100644 index 000000000..3e863c578 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json @@ -0,0 +1,1898 @@ +{ + "formatVersion": 1, + "database": { + "version": 29, + "identityHash": "30e4647c7dd84a6ac9b2f9f3ef7d3264", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `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)", + "fields": [ + { + "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 + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `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)", + "fields": [ + { + "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 + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `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)", + "fields": [ + { + "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 + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '30e4647c7dd84a6ac9b2f9f3ef7d3264')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/30.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/30.json new file mode 100644 index 000000000..309f4ac09 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/30.json @@ -0,0 +1,1954 @@ +{ + "formatVersion": 1, + "database": { + "version": 30, + "identityHash": "891980e378373d0a17bd341f9b07cc74", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "current", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `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)", + "fields": [ + { + "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 + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `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)", + "fields": [ + { + "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 + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradePartialStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradeSemesterStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasAttachments", + "columnName": "has_attachments", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MessageAttachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))", + "fields": [ + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "oneDriveId", + "columnName": "one_drive_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filename", + "columnName": "filename", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "real_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `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)", + "fields": [ + { + "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 + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isDone", + "columnName": "is_done", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Conferences", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "TimetableAdditional", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '891980e378373d0a17bd341f9b07cc74')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt index 611161e58..aca287322 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.data.db.migrations +import android.content.Context import androidx.preference.PreferenceManager import androidx.room.Room import androidx.room.testing.MigrationTestHelper @@ -22,10 +23,11 @@ abstract class AbstractMigrationTest { ) fun getMigratedRoomDatabase(): AppDatabase { + val context = ApplicationProvider.getApplicationContext() val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java, dbName) .addMigrations(*AppDatabase.getMigrations(SharedPrefProvider(PreferenceManager - .getDefaultSharedPreferences(ApplicationProvider.getApplicationContext()))) + .getDefaultSharedPreferences(context))) ) .build() // close the database and release any stream resources when the test finishes diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestEntityCreator.kt deleted file mode 100644 index 04d13be4b..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestEntityCreator.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.wulkanowy.data.repositories - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import java.time.LocalDate.now -import java.time.LocalDateTime - -fun getStudent(): Student { - return Student( - email = "test", - password = "test123", - schoolSymbol = "23", - scrapperBaseUrl = "fakelog.cf", - loginType = "AUTO", - isCurrent = true, - userName = "", - studentName = "", - schoolShortName = "", - schoolName = "", - studentId = 0, - classId = 1, - symbol = "", - registrationDate = LocalDateTime.now(), - className = "", - loginMode = "API", - certificateKey = "", - privateKey = "", - mobileBaseUrl = "", - userLoginId = 0, - isParent = false - ) -} - -fun getSemester() = Semester( - semesterId = 1, - studentId = 1, - classId = 1, - diaryId = 2, - diaryName = "", - end = now(), - schoolYear = 2019, - semesterName = 1, - start = now(), - unitId = 1 -) diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt deleted file mode 100644 index fa1289869..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt +++ /dev/null @@ -1,83 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import java.time.LocalDate -import java.time.LocalDate.now -import java.time.LocalDate.of -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class AttendanceLocalTest { - - private lateinit var attendanceLocal: AttendanceLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build() - attendanceLocal = AttendanceLocal(testDb.attendanceDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - val list = listOf( - getAttendanceEntity( - of(2018, 9, 10), - SentExcuseStatus.ACCEPTED - ), - getAttendanceEntity( - of(2018, 9, 14), - SentExcuseStatus.WAITING - ), - getAttendanceEntity( - of(2018, 9, 17), - SentExcuseStatus.ACCEPTED - ) - ) - runBlocking { attendanceLocal.saveAttendance(list) } - - val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1) - val attendance = runBlocking { attendanceLocal.getAttendance(semester, of(2018, 9, 10), of(2018, 9, 14)).first() } - assertEquals(2, attendance.size) - assertEquals(attendance[0].date, of(2018, 9, 10)) - assertEquals(attendance[1].date, of(2018, 9, 14)) - } - - private fun getAttendanceEntity( - date: LocalDate, - excuseStatus: SentExcuseStatus - ) = Attendance( - studentId = 1, - diaryId = 2, - timeId = 3, - date = date, - number = 0, - subject = "", - name = "", - presence = false, - absence = false, - exemption = false, - lateness = false, - excused = false, - deleted = false, - excusable = false, - excuseStatus = excuseStatus.name - ) -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt deleted file mode 100644 index ca7d0b1be..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocalTest.kt +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import java.time.LocalDate -import java.time.LocalDate.now -import java.time.LocalDate.of -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class CompletedLessonsLocalTest { - - private lateinit var completedLessonsLocal: CompletedLessonsLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room - .inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - completedLessonsLocal = CompletedLessonsLocal(testDb.completedLessonsDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - val list = listOf( - getCompletedLesson(of(2018, 9, 10), 1), - getCompletedLesson(of(2018, 9, 14), 2), - getCompletedLesson(of(2018, 9, 17), 3) - ) - runBlocking { completedLessonsLocal.saveCompletedLessons(list) } - - val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1) - val completed = runBlocking { completedLessonsLocal.getCompletedLessons(semester, of(2018, 9, 10), of(2018, 9, 14)).first() } - assertEquals(2, completed.size) - assertEquals(completed[0].date, of(2018, 9, 10)) - assertEquals(completed[1].date, of(2018, 9, 14)) - } - - private fun getCompletedLesson(date: LocalDate, number: Int): CompletedLesson { - return CompletedLesson(1, 2, date, number, "", "", "", "", "", "", "") - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt deleted file mode 100644 index 14b29c9f6..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/exam/ExamLocalTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import java.time.LocalDate.now -import java.time.LocalDate.of -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class ExamLocalTest { - - private lateinit var examLocal: ExamLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java).build() - examLocal = ExamLocal(testDb.examsDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - val list = listOf( - Exam(1, 2, of(2018, 9, 10), now(), "", "", "", "", "", ""), - Exam(1, 2, of(2018, 9, 14), now(), "", "", "", "", "", ""), - Exam(1, 2, of(2018, 9, 17), now(), "", "", "", "", "", "") - ) - runBlocking { examLocal.saveExams(list) } - - val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1) - val exams = runBlocking { examLocal.getExams(semester, of(2018, 9, 10), of(2018, 9, 14)).first() } - assertEquals(2, exams.size) - assertEquals(exams[0].date, of(2018, 9, 10)) - assertEquals(exams[1].date, of(2018, 9, 14)) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt deleted file mode 100644 index 946ebf8ea..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeLocalTest.kt +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import java.time.LocalDate -import java.time.LocalDate.now -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class GradeLocalTest { - - private lateinit var gradeLocal: GradeLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room - .inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - val list = listOf( - createGradeLocal(5, 3.0, LocalDate.of(2018, 9, 10), "", 1), - createGradeLocal(4, 4.0, LocalDate.of(2019, 2, 27), "", 2), - createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2) - ) - runBlocking { gradeLocal.saveGrades(list) } - - val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1) - - val grades = runBlocking { gradeLocal.getGradesDetails(semester).first() } - - assertEquals(2, grades.size) - assertEquals(grades[0].date, LocalDate.of(2019, 2, 27)) - assertEquals(grades[1].date, LocalDate.of(2019, 2, 28)) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt deleted file mode 100644 index 5cf9c2196..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt +++ /dev/null @@ -1,221 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import android.os.Build.VERSION_CODES.P -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider.getApplicationContext -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SdkSuppress -import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.mockk.MockKAnnotations -import io.mockk.coEvery -import io.mockk.every -import io.mockk.impl.annotations.MockK -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import java.time.LocalDate.of -import java.time.LocalDateTime -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue - -@SdkSuppress(minSdkVersion = P) -@RunWith(AndroidJUnit4::class) -class GradeRepositoryTest { - - @MockK - private lateinit var semesterMock: Semester - - private lateinit var studentMock: Student - - @MockK - private lateinit var gradeRemote: GradeRemote - - private lateinit var gradeLocal: GradeLocal - - private lateinit var testDb: AppDatabase - - @Before - fun initApi() { - MockKAnnotations.init(this) - testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() - gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao) - studentMock = getStudentMock() - - every { semesterMock.studentId } returns 1 - every { semesterMock.diaryId } returns 1 - every { semesterMock.schoolYear } returns 2019 - every { semesterMock.semesterId } returns 1 - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun markOlderThanRegisterDateAsRead() { - coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf( - createGradeLocal(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"), - createGradeLocal(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"), - createGradeLocal(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"), - createGradeLocal(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza") - ) to emptyList()) - - val grades = runBlocking { - GradeRepository(gradeLocal, gradeRemote) - .getGrades(studentMock, semesterMock, true) - .filter { it.status == Status.SUCCESS }.first().data!!.first.sortedByDescending { it.date } - } - - assertFalse { grades[0].isRead } - assertFalse { grades[1].isRead } - assertTrue { grades[2].isRead } - assertTrue { grades[3].isRead } - } - - @Test - fun mitigateOldGradesNotifications() { - val list = listOf( - createGradeLocal(5, 3.0, of(2019, 2, 25), "Jedna ocena"), - createGradeLocal(4, 4.0, of(2019, 2, 26), "Druga"), - createGradeLocal(3, 5.0, of(2019, 2, 27), "Trzecia") - ) - runBlocking { gradeLocal.saveGrades(list) } - - coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf( - createGradeLocal(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"), - createGradeLocal(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"), - createGradeLocal(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"), - createGradeLocal(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa") - ) to emptyList()) - - val grades = runBlocking { - GradeRepository(gradeLocal, gradeRemote) - .getGrades(studentMock, semesterMock, true) - .filter { it.status == Status.SUCCESS }.first().data!!.first.sortedByDescending { it.date } - } - - assertFalse { grades[0].isRead } - assertFalse { grades[1].isRead } - assertTrue { grades[2].isRead } - assertTrue { grades[3].isRead } - } - - @Test - fun subtractLocaleDuplicateGrades() { - val list = listOf( - createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), - createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), - createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") - ) - runBlocking { gradeLocal.saveGrades(list) } - - coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf( - createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), - createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") - ) to emptyList()) - - val grades = runBlocking { - GradeRepository(gradeLocal, gradeRemote) - .getGrades(studentMock, semesterMock, true) - .filter { it.status == Status.SUCCESS }.first().data!! - } - - assertEquals(2, grades.first.size) - } - - @Test - fun subtractRemoteDuplicateGrades() { - val list = listOf( - createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), - createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") - ) - runBlocking { gradeLocal.saveGrades(list) } - - coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf( - createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), - createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), - createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") - ) to emptyList()) - - val grades = runBlocking { - GradeRepository(gradeLocal, gradeRemote) - .getGrades(studentMock, semesterMock, true) - .filter { it.status == Status.SUCCESS }.first().data!! - } - - assertEquals(3, grades.first.size) - } - - @Test - fun emptyLocal() { - runBlocking { gradeLocal.saveGrades(listOf()) } - - coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf( - createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), - createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), - createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") - ) to emptyList()) - - val grades = runBlocking { - GradeRepository(gradeLocal, gradeRemote) - .getGrades(studentMock, semesterMock, true) - .filter { it.status == Status.SUCCESS }.first().data!! - } - - assertEquals(3, grades.first.size) - } - - @Test - fun emptyRemote() { - val list = listOf( - createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), - createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") - ) - runBlocking { gradeLocal.saveGrades(list) } - - coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (emptyList() to emptyList()) - - val grades = runBlocking { - GradeRepository(gradeLocal, gradeRemote) - .getGrades(studentMock, semesterMock, true) - .filter { it.status == Status.SUCCESS }.first().data!! - } - - assertEquals(0, grades.first.size) - } - - private fun getStudentMock() = Student( - scrapperBaseUrl = "http://fakelog.cf", - email = "jan@fakelog.cf", - certificateKey = "", - classId = 0, - className = "", - isCurrent = false, - isParent = false, - loginMode = Sdk.Mode.SCRAPPER.name, - loginType = "STANDARD", - mobileBaseUrl = "", - password = "", - privateKey = "", - registrationDate = LocalDateTime.of(2019, 2, 27, 12, 0), - schoolName = "", - schoolShortName = "test", - schoolSymbol = "", - studentId = 0, - studentName = "", - symbol = "", - userLoginId = 0, - userName = "" - ) -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt deleted file mode 100644 index 629c24321..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import java.time.LocalDate -import io.github.wulkanowy.sdk.pojo.Grade as GradeRemote -import io.github.wulkanowy.data.db.entities.Grade as GradeLocal - -fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String, semesterId: Int = 1): GradeLocal { - return GradeLocal( - semesterId = semesterId, - studentId = 1, - modifier = .0, - teacher = "", - subject = "", - date = date, - color = "", - comment = "", - description = desc, - entry = "", - gradeSymbol = "", - value = value.toDouble(), - weight = "", - weightValue = weight - ) -} - -fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String): GradeRemote { - return GradeRemote( - subject = "", - color = "", - comment = "", - date = date, - description = desc, - entry = "", - modifier = .0, - symbol = "", - teacher = "", - value = value.toDouble(), - weight = weight.toString(), - weightValue = weight - ) -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt deleted file mode 100644 index 197d2d0ec..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt +++ /dev/null @@ -1,112 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.GradePointsStatistics -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import java.time.LocalDate.now -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class GradeStatisticsLocalTest { - - private lateinit var gradeStatisticsLocal: GradeStatisticsLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradeStatistics, testDb.gradePointsStatistics) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndRead_subject() { - val list = listOf( - getGradeStatistics("Matematyka", 2, 1), - getGradeStatistics("Fizyka", 1, 2) - ) - runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) } - - val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() } - assertEquals(1, stats.size) - assertEquals(stats[0].subject, "Matematyka") - } - - @Test - fun saveAndRead_all() { - val list = listOf( - getGradeStatistics("Matematyka", 2, 1), - getGradeStatistics("Chemia", 2, 1), - getGradeStatistics("Fizyka", 1, 2) - ) - runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) } - - val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() } - assertEquals(2, stats.size) -// assertEquals(3, stats.size) -// assertEquals(stats[0].subject, "Wszystkie") // now in main repo - assertEquals(stats[0].subject, "Matematyka") - assertEquals(stats[1].subject, "Chemia") - } - - @Test - fun saveAndRead_points() { - val list = listOf( - getGradePointsStatistics("Matematyka", 2, 1), - getGradePointsStatistics("Chemia", 2, 1), - getGradePointsStatistics("Fizyka", 1, 2) - ) - runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(list) } - - val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() } - with(stats[0]) { - assertEquals(subject, "Matematyka") - assertEquals(others, 5.0) - assertEquals(student, 5.0) - } - } - - @Test - fun saveAndRead_subjectEmpty() { - runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) } - - val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() } - assertEquals(emptyList(), stats) - } - - @Test - fun saveAndRead_allEmpty() { - runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) } - - val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() } - assertEquals(emptyList(), stats) - } - - private fun getSemester(): Semester { - return Semester(2, 2, "", 2019, 1, 2, now(), now(), 1, 1) - } - - private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics { - return GradeStatistics(studentId, semesterId, subject, 5, 5, false) - } - - private fun getGradePointsStatistics(subject: String, studentId: Int, semesterId: Int): GradePointsStatistics { - return GradePointsStatistics(studentId, semesterId, subject, 5.0, 5.0) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt deleted file mode 100644 index ca38b1fee..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocalTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Student -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import java.time.LocalDate -import java.time.LocalDateTime.now -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class LuckyNumberLocalTest { - - private lateinit var luckyNumberLocal: LuckyNumberLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room - .inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - luckyNumberLocal = LuckyNumberLocal(testDb.luckyNumberDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - val number = LuckyNumber(1, LocalDate.of(2019, 1, 20), 14) - runBlocking { luckyNumberLocal.saveLuckyNumber(number) } - - val student = Student("", "", "", "", "", "", false, "", "", "", 1, 1, "", "", "", "", "", "", 1, false, now()) - val luckyNumber = runBlocking { luckyNumberLocal.getLuckyNumber(student, LocalDate.of(2019, 1, 20)).first() } - - assertEquals(1, luckyNumber?.studentId) - assertEquals(LocalDate.of(2019, 1, 20), luckyNumber?.date) - assertEquals(14, luckyNumber?.luckyNumber) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt deleted file mode 100644 index 6bd1959bd..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt +++ /dev/null @@ -1,65 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import java.time.LocalDateTime -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class RecipientLocalTest { - - private lateinit var recipientLocal: RecipientLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - recipientLocal = RecipientLocal(testDb.recipientDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - val list = listOf( - Recipient(1, "2rPracownik", "Kowalski Jan", "Kowalski Jan [KJ] - Pracownik (Fake123456)", 3, 4, 2, "hash"), - Recipient(1, "3rPracownik", "Kowalska Karolina", "Kowalska Karolina [KK] - Pracownik (Fake123456)", 4, 4, 2, "hash"), - Recipient(1, "4rPracownik", "Krupa Stanisław", "Krupa Stanisław [KS] - Uczeń (Fake123456)", 5, 4, 1, "hash") - ) - runBlocking { recipientLocal.saveRecipients(list) } - - val student = Student("fakelog.cf", "AUTO", "", "", "", "", false, "", "", "", 1, 0, "", "", "", "", "", "", 1, true, LocalDateTime.now()) - val recipients = runBlocking { - recipientLocal.getRecipients( - student = student, - role = 2, - unit = ReportingUnit(1, 4, "", 0, "", emptyList()) - ) - } - - assertEquals(2, recipients.size) - assertEquals(1, recipients[0].studentId) - assertEquals("3rPracownik", recipients[1].realId) - assertEquals("Kowalski Jan", recipients[0].name) - assertEquals("Kowalska Karolina [KK] - Pracownik (Fake123456)", recipients[1].realName) - assertEquals(3, recipients[0].loginId) - assertEquals(4, recipients[1].unitId) - assertEquals(2, recipients[0].role) - assertEquals("hash", recipients[1].hash) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt deleted file mode 100644 index d68f15a80..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import android.content.Context -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.TestDispatchersProvider -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.repositories.getStudent -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class StudentLocalTest { - - private lateinit var studentLocal: StudentLocal - - private lateinit var testDb: AppDatabase - - private val student = getStudent() - - @Before - fun createDb() { - val context = ApplicationProvider.getApplicationContext() - testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) - .build() - studentLocal = StudentLocal(testDb.studentDao, TestDispatchersProvider(), context) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - runBlocking { studentLocal.saveStudents(listOf(student)) } - - val student = runBlocking { studentLocal.getCurrentStudent(true) } - assertEquals("23", student?.schoolSymbol) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt deleted file mode 100644 index dddf6464c..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt +++ /dev/null @@ -1,48 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import java.time.LocalDateTime -import java.time.LocalDateTime.now -import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal -import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote - -fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal { - return TimetableLocal( - studentId = 1, - diaryId = 2, - number = number, - start = start, - end = now(), - date = start.toLocalDate(), - subject = subject, - subjectOld = "", - group = "", - room = room, - roomOld = "", - teacher = teacher, - teacherOld = "", - info = "", - isStudentPlan = true, - changes = changes, - canceled = false - ) -} - -fun createTimetableRemote(start: LocalDateTime, number: Int = 1, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableRemote { - return TimetableRemote( - number = number, - start = start, - end = start.plusMinutes(45), - date = start.toLocalDate(), - subject = subject, - group = "", - room = room, - teacher = teacher, - info = "", - changes = changes, - canceled = false, - roomOld = "", - subjectOld = "", - teacherOld = "", - studentPlan = true - ) -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt deleted file mode 100644 index e793212e6..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import java.time.LocalDate -import java.time.LocalDateTime.of -import kotlin.test.assertEquals - -@RunWith(AndroidJUnit4::class) -class TimetableLocalTest { - - private lateinit var timetableDb: TimetableLocal - - private lateinit var testDb: AppDatabase - - @Before - fun createDb() { - testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) - .build() - timetableDb = TimetableLocal(testDb.timetableDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun saveAndReadTest() { - val list = listOf( - createTimetableLocal(of(2018, 9, 10, 0, 0, 0), 1), - createTimetableLocal(of(2018, 9, 14, 0, 0, 0), 1), - createTimetableLocal(of(2018, 9, 17, 0, 0, 0), 1) - ) - runBlocking { timetableDb.saveTimetable(list) } - - val semester = Semester(1, 2, "", 1, 1, 2019, LocalDate.now(), LocalDate.now(), 1, 1) - val exams = runBlocking { - timetableDb.getTimetable( - semester = semester, - startDate = LocalDate.of(2018, 9, 10), - endDate = LocalDate.of(2018, 9, 14) - ).first() - } - - assertEquals(2, exams.size) - assertEquals(exams[0].date, LocalDate.of(2018, 9, 10)) - assertEquals(exams[1].date, LocalDate.of(2018, 9, 14)) - } -} diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt deleted file mode 100644 index 1bd3c4679..000000000 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt +++ /dev/null @@ -1,155 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import android.os.Build.VERSION_CODES.P -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider.getApplicationContext -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SdkSuppress -import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.db.AppDatabase -import io.github.wulkanowy.data.repositories.getSemester -import io.github.wulkanowy.data.repositories.getStudent -import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper -import io.mockk.MockKAnnotations -import io.mockk.coEvery -import io.mockk.impl.annotations.MockK -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import java.time.LocalDate -import java.time.LocalDateTime.of -import kotlin.test.assertEquals - -@SdkSuppress(minSdkVersion = P) -@RunWith(AndroidJUnit4::class) -class TimetableRepositoryTest { - - @MockK(relaxed = true) - private lateinit var timetableNotificationSchedulerHelper: TimetableNotificationSchedulerHelper - - @MockK - private lateinit var timetableRemote: TimetableRemote - - private lateinit var timetableLocal: TimetableLocal - - private lateinit var testDb: AppDatabase - - private val student = getStudent() - - private val semester = getSemester() - - @Before - fun initApi() { - MockKAnnotations.init(this) - testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() - timetableLocal = TimetableLocal(testDb.timetableDao) - } - - @After - fun closeDb() { - testDb.close() - } - - @Test - fun copyRoomToCompletedFromPrevious() { - runBlocking { - timetableLocal.saveTimetable(listOf( - createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "123", "Przyroda"), - createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "321", "Religia"), - createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "213", "W-F"), - createTimetableLocal(of(2019, 3, 5, 10, 30), 3, "213", "W-F", "Jan Kowalski") - )) - } - - coEvery { timetableRemote.getTimetable(student, semester, any(), any()) } returns listOf( - createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "", "Przyroda"), - createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "", "Religia"), - createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "", "W-F"), - createTimetableLocal(of(2019, 3, 5, 10, 30), 4, "", "W-F") - ) - - val lessons = runBlocking { - TimetableRepository(timetableLocal, timetableRemote, timetableNotificationSchedulerHelper).getTimetable( - student = student, - semester = semester, - start = LocalDate.of(2019, 3, 5), - end = LocalDate.of(2019, 3, 5), - forceRefresh = true - ).filter { it.status == Status.SUCCESS }.first().data.orEmpty() - } - - assertEquals(4, lessons.size) - assertEquals("123", lessons[0].room) - assertEquals("321", lessons[1].room) - assertEquals("213", lessons[2].room) - } - - @Test - fun copyTeacherToCompletedFromPrevious() { - val list = listOf( - createTimetableLocal(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false), - createTimetableLocal(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Paweł Poniedziałkowski", false), - createTimetableLocal(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Wtorkowska", true), - createTimetableLocal(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true), - - createTimetableLocal(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "Joanna Wtorkowska", false), - createTimetableLocal(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "Joanna Wtorkowska", false), - createTimetableLocal(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "Joanna Środowska", true), - createTimetableLocal(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "Joanna Środowska", true), - - createTimetableLocal(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "", false), - createTimetableLocal(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "", false), - createTimetableLocal(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "", true), - createTimetableLocal(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "", true) - ) - runBlocking { timetableLocal.saveTimetable(list) } - - coEvery { timetableRemote.getTimetable(student, semester, any(), any()) } returns listOf( - createTimetableLocal(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false), - createTimetableLocal(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Jakub Wtorkowski", true), - createTimetableLocal(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Poniedziałkowska", false), - createTimetableLocal(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true), - - createTimetableLocal(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "", false), - createTimetableLocal(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "", true), - createTimetableLocal(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "", false), - createTimetableLocal(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "", true), - - createTimetableLocal(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "Paweł Środowski", false), - createTimetableLocal(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "Paweł Czwartkowski", true), - createTimetableLocal(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "Paweł Środowski", false), - createTimetableLocal(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true) - ) - - val lessons = runBlocking { - TimetableRepository(timetableLocal, timetableRemote, timetableNotificationSchedulerHelper).getTimetable( - student = student, - semester = semester, - start = LocalDate.of(2019, 12, 23), - end = LocalDate.of(2019, 12, 25), - forceRefresh = true - ).filter { it.status == Status.SUCCESS }.first().data.orEmpty() - } - - assertEquals(12, lessons.size) - - assertEquals("Paweł Poniedziałkowski", lessons[0].teacher) - assertEquals("Jakub Wtorkowski", lessons[1].teacher) - assertEquals("Joanna Poniedziałkowska", lessons[2].teacher) - assertEquals("Joanna Wtorkowska", lessons[3].teacher) - - assertEquals("Joanna Wtorkowska", lessons[4].teacher) - assertEquals("", lessons[5].teacher) - assertEquals("", lessons[6].teacher) - assertEquals("", lessons[7].teacher) - - assertEquals("Paweł Środowski", lessons[8].teacher) - assertEquals("Paweł Czwartkowski", lessons[9].teacher) - assertEquals("Paweł Środowski", lessons[10].teacher) - assertEquals("Paweł Czwartkowski", lessons[11].teacher) - } -} diff --git a/app/src/debug/agconnect-services.json b/app/src/debug/agconnect-services.json new file mode 100644 index 000000000..48192df01 --- /dev/null +++ b/app/src/debug/agconnect-services.json @@ -0,0 +1,33 @@ +{ + "agcgw":{ + "backurl":"connect-dre.dbankcloud.cn", + "url":"connect-dre.hispace.hicloud.com" + }, + "client":{ + "cp_id":"890048000024105546", + "product_id":"", + "client_id":"", + "client_secret":"", + "app_id":"101440411", + "package_name":"io.github.wulkanowy.dev", + "api_key":"" + }, + "service":{ + "analytics":{ + "collector_url":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", + "resource_id":"p1", + "channel_id":"" + }, + "search":{ + "url":"https://search-dre.cloud.huawei.com" + }, + "cloudstorage":{ + "storage_url":"https://ops-dre.agcstorage.link" + }, + "ml":{ + "mlservice_url":"ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn" + } + }, + "region":"DE", + "configuration_version":"1.0" +} diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt similarity index 75% rename from app/src/fdroid/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt rename to app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt index f23645bc3..3bf7e1693 100644 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/FirebaseAnalyticsHelper.kt +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -6,7 +6,7 @@ import javax.inject.Singleton @Singleton @Suppress("UNUSED_PARAMETER") -class FirebaseAnalyticsHelper @Inject constructor() { +class AnalyticsHelper @Inject constructor() { fun logEvent(name: String, vararg params: Pair) { // do nothing @@ -15,4 +15,8 @@ class FirebaseAnalyticsHelper @Inject constructor() { 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/CrashlyticsUtils.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/CrashLogUtils.kt similarity index 71% rename from app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt rename to app/src/fdroid/java/io/github/wulkanowy/utils/CrashLogUtils.kt index d03a319a2..5d58270d4 100644 --- a/app/src/fdroid/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt +++ b/app/src/fdroid/java/io/github/wulkanowy/utils/CrashLogUtils.kt @@ -8,6 +8,6 @@ open class TimberTreeNoOp : Timber.Tree() { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {} } -class CrashlyticsTree : TimberTreeNoOp() +class CrashLogTree : TimberTreeNoOp() -class CrashlyticsExceptionTree : TimberTreeNoOp() +class CrashLogExceptionTree : TimberTreeNoOp() 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/AnalyticsHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt new file mode 100644 index 000000000..5d33825f1 --- /dev/null +++ b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.utils + +import android.app.Activity +import android.content.Context +import android.os.Bundle +import com.huawei.hms.analytics.HiAnalytics +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AnalyticsHelper @Inject constructor( + @ApplicationContext private val context: Context +) { + + private val analytics by lazy { HiAnalytics.getInstance(context) } + + fun logEvent(name: String, vararg params: Pair) { + Bundle().apply { + params.forEach { + if (it.second == null) return@forEach + when (it.second) { + is String, is String? -> putString(it.first, it.second as String) + is Int, is Int? -> putInt(it.first, it.second as Int) + is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean) + } + } + analytics.onEvent(name, this) + } + } + + fun setCurrentScreen(activity: Activity, name: String?) { + analytics.pageStart(name, activity::class.simpleName) + } + + fun popCurrentScreen(name: String?) { + analytics.pageEnd(name) + } +} diff --git a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt new file mode 100644 index 000000000..b5fb6ad71 --- /dev/null +++ b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.utils + +import android.util.Log +import com.huawei.agconnect.crash.AGConnectCrash +import fr.bipi.tressence.base.FormatterPriorityTree +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException +import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException +import java.io.InterruptedIOException +import java.net.SocketTimeoutException +import java.net.UnknownHostException + +class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) { + + private val connectCrash by lazy { AGConnectCrash.getInstance() } + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + if (skipLog(priority, tag, message, t)) return + + connectCrash.log(format(priority, tag, message)) + } +} + +class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR) { + + private val connectCrash by lazy { AGConnectCrash.getInstance() } + + override fun skipLog(priority: Int, tag: String?, message: String, t: Throwable?): Boolean { + return when (t) { + is FeatureDisabledException, + is FeatureNotAvailableException, + is UnknownHostException, + is SocketTimeoutException, + is InterruptedIOException -> true + else -> super.skipLog(priority, tag, message, t) + } + } + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + if (skipLog(priority, tag, message, t)) return + + // 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/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 4ec2f7816..eaef43694 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,6 +9,17 @@ + + + + + + + + + + + + + { return arrayOf( @@ -142,6 +154,9 @@ abstract class AppDatabase : RoomDatabase() { Migration25(), Migration26(), Migration27(), + Migration28(), + Migration29(), + Migration30(), ) } @@ -171,9 +186,11 @@ abstract class AppDatabase : RoomDatabase() { abstract val gradeSummaryDao: GradeSummaryDao - abstract val gradeStatistics: GradeStatisticsDao + abstract val gradePartialStatisticsDao: GradePartialStatisticsDao - abstract val gradePointsStatistics: GradePointsStatisticsDao + abstract val gradePointsStatisticsDao: GradePointsStatisticsDao + + abstract val gradeSemesterStatisticsDao: GradeSemesterStatisticsDao abstract val messagesDao: MessagesDao @@ -198,4 +215,8 @@ abstract class AppDatabase : RoomDatabase() { abstract val teacherDao: TeacherDao abstract val schoolDao: SchoolDao + + abstract val conferenceDao: ConferenceDao + + abstract val timetableAdditionalDao: TimetableAdditionalDao } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt index 4a4aaf74f..9301d5fa9 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt @@ -6,7 +6,9 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class SharedPrefProvider @Inject constructor(private val sharedPref: SharedPreferences) { +class SharedPrefProvider @Inject constructor( + private val sharedPref: SharedPreferences +) { companion object { const val APP_VERSION_CODE_KEY = "app_version_code" diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt index 8c03609d6..097ad7c81 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt @@ -12,5 +12,5 @@ import javax.inject.Singleton interface CompletedLessonsDao : BaseDao { @Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") - fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow> + fun loadAll(studentId: Int, diaryId: Int, from: LocalDate, end: LocalDate): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt new file mode 100644 index 000000000..4ed9aecf5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.Conference +import kotlinx.coroutines.flow.Flow +import javax.inject.Singleton + +@Dao +@Singleton +interface ConferenceDao : BaseDao { + + @Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId") + fun loadAll(diaryId: Int, studentId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePartialStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePartialStatisticsDao.kt new file mode 100644 index 000000000..bce6ce572 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePartialStatisticsDao.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.GradePartialStatistics +import kotlinx.coroutines.flow.Flow + +@Dao +interface GradePartialStatisticsDao : BaseDao { + + @Query("SELECT * FROM GradePartialStatistics WHERE student_id = :studentId AND semester_id = :semesterId") + fun loadAll(semesterId: Int, studentId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSemesterStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSemesterStatisticsDao.kt new file mode 100644 index 000000000..09ae81714 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSemesterStatisticsDao.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics +import kotlinx.coroutines.flow.Flow + +@Dao +interface GradeSemesterStatisticsDao : BaseDao { + + @Query("SELECT * FROM GradeSemesterStatistics WHERE student_id = :studentId AND semester_id = :semesterId") + fun loadAll(semesterId: Int, studentId: Int): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt deleted file mode 100644 index b462ad5db..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.wulkanowy.data.db.dao - -import androidx.room.Dao -import androidx.room.Query -import io.github.wulkanowy.data.db.entities.GradeStatistics -import kotlinx.coroutines.flow.Flow -import javax.inject.Singleton - -@Singleton -@Dao -interface GradeStatisticsDao : BaseDao { - - @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester") - fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Flow> - - @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND is_semester = :isSemester") - fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Flow> -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt index 8baba2c30..081e859a5 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt @@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow @Dao interface MobileDeviceDao : BaseDao { - @Query("SELECT * FROM MobileDevices WHERE student_id = :studentId ORDER BY date DESC") - fun loadAll(studentId: Int): Flow> + @Query("SELECT * FROM MobileDevices WHERE student_id = :userLoginId ORDER BY date DESC") + fun loadAll(userLoginId: Int): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt index 419efde0d..943f3f0cc 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt @@ -9,6 +9,6 @@ import javax.inject.Singleton @Dao interface RecipientDao : BaseDao { - @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND role = :role AND unit_id = :unitId") - suspend fun load(studentId: Int, role: Int, unitId: Int): List + @Query("SELECT * FROM Recipients WHERE student_id = :userLoginId AND unit_id = :unitId AND role = :role") + suspend fun loadAll(userLoginId: Int, unitId: Int, role: Int): List } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableAdditionalDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableAdditionalDao.kt new file mode 100644 index 000000000..335e003e1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableAdditionalDao.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Query +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import kotlinx.coroutines.flow.Flow +import java.time.LocalDate +import javax.inject.Singleton + +@Dao +@Singleton +interface TimetableAdditionalDao : BaseDao { + + @Query("SELECT * FROM TimetableAdditional WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") + fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow> +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt new file mode 100644 index 000000000..8ddcbbb0b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt @@ -0,0 +1,35 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDateTime + +@Entity(tableName = "Conferences") +data class Conference( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val title: String, + + val subject: String, + + val agenda: String, + + @ColumnInfo(name = "present_on_conference") + val presentOnConference: String, + + @ColumnInfo(name = "conference_id") + val conferenceId: Int, + + val date: LocalDateTime +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePartialStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePartialStatistics.kt new file mode 100644 index 000000000..db164afde --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePartialStatistics.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "GradePartialStatistics") +data class GradePartialStatistics( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "semester_id") + val semesterId: Int, + + val subject: String, + + @ColumnInfo(name = "class_average") + val classAverage: String, + + @ColumnInfo(name = "student_average") + val studentAverage: String, + + @ColumnInfo(name = "class_amounts") + val classAmounts: List, + + @ColumnInfo(name = "student_amounts") + val studentAmounts: List + +) { + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt similarity index 61% rename from app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt rename to app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt index 8ad8b8b8d..e747271ce 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt @@ -4,8 +4,8 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey -@Entity(tableName = "GradesStatistics") -data class GradeStatistics( +@Entity(tableName = "GradeSemesterStatistics") +data class GradeSemesterStatistics( @ColumnInfo(name = "student_id") val studentId: Int, @@ -15,13 +15,14 @@ data class GradeStatistics( val subject: String, - val grade: Int, + val amounts: List, - val amount: Int, - - @ColumnInfo(name = "is_semester") - val semester: Boolean + @ColumnInfo(name = "student_grade") + val studentGrade: Int ) { @PrimaryKey(autoGenerate = true) var id: Long = 0 + + @Transient + var average: String = "" } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt index 9d8f11625..83d82c0b9 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt @@ -10,7 +10,7 @@ import java.time.LocalDateTime data class MobileDevice( @ColumnInfo(name = "student_id") - val studentId: Int, + val userLoginId: Int, @ColumnInfo(name = "device_id") val deviceId: Int, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt index 3021da72d..b1f1f3530 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt @@ -9,7 +9,7 @@ import java.io.Serializable data class Recipient( @ColumnInfo(name = "student_id") - val studentId: Int, + val userLoginId: Int, @ColumnInfo(name = "real_id") val realId: String, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt index 601d8aac7..0570a2ffd 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt @@ -12,7 +12,7 @@ data class ReportingUnit( val studentId: Int, @ColumnInfo(name = "real_id") - val realId: Int, + val unitId: Int, @ColumnInfo(name = "short") val shortName: String, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt new file mode 100644 index 000000000..c1f1365f9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable +import java.time.LocalDate +import java.time.LocalDateTime + +@Entity(tableName = "TimetableAdditional") +data class TimetableAdditional( + + @ColumnInfo(name = "student_id") + val studentId: Int, + + @ColumnInfo(name = "diary_id") + val diaryId: Int, + + val start: LocalDateTime, + + val end: LocalDateTime, + + val date: LocalDate, + + val subject: String, +) : Serializable { + + @PrimaryKey(autoGenerate = true) + var id: Long = 0 +} 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 new file mode 100644 index 000000000..51e7628b5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration28 : Migration(27, 28) { + + 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, + 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 + ) + """) + } +} 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 new file mode 100644 index 000000000..327552d75 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration29 : Migration(28, 29) { + + 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, + semester_id INTEGER NOT NULL, + subject TEXT NOT NULL, + amounts TEXT NOT NULL, + student_grade INTEGER NOT NULL + ) + """) + database.execSQL(""" + CREATE TABLE IF NOT EXISTS GradePartialStatistics ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + 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 + ) + """) + } +} 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 new file mode 100644 index 000000000..b33914fec --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration30.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration30 : Migration(29, 30) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE TimetableAdditional ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + 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 + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt b/app/src/main/java/io/github/wulkanowy/data/enums/MessageFolder.kt similarity index 63% rename from app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt rename to app/src/main/java/io/github/wulkanowy/data/enums/MessageFolder.kt index 06f5a1e05..899ba9085 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageFolder.kt +++ b/app/src/main/java/io/github/wulkanowy/data/enums/MessageFolder.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.data.repositories.message +package io.github.wulkanowy.data.enums enum class MessageFolder(val id: Int = 1) { RECEIVED(1), diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/SentExcuseStatus.kt b/app/src/main/java/io/github/wulkanowy/data/enums/SentExcuseStatus.kt similarity index 60% rename from app/src/main/java/io/github/wulkanowy/data/repositories/attendance/SentExcuseStatus.kt rename to app/src/main/java/io/github/wulkanowy/data/enums/SentExcuseStatus.kt index 50d6b8ed5..99878152f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/SentExcuseStatus.kt +++ b/app/src/main/java/io/github/wulkanowy/data/enums/SentExcuseStatus.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.data.repositories.attendance +package io.github.wulkanowy.data.enums enum class SentExcuseStatus(val id: Int = 0) { WAITING, 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 new file mode 100644 index 000000000..46e67fdaa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt @@ -0,0 +1,43 @@ +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.sdk.pojo.Attendance as SdkAttendance +import io.github.wulkanowy.sdk.pojo.AttendanceSummary as SdkAttendanceSummary + +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, + name = it.name, + presence = it.presence, + absence = it.absence, + exemption = it.exemption, + lateness = it.lateness, + excused = it.excused, + deleted = it.deleted, + excusable = it.excusable, + excuseStatus = it.excuseStatus?.name + ) +} + +fun List.mapToEntities(semester: Semester, subjectId: Int) = map { + AttendanceSummary( + studentId = semester.studentId, + diaryId = semester.diaryId, + subjectId = subjectId, + month = it.month, + presence = it.presence, + absence = it.absence, + absenceExcused = it.absenceExcused, + absenceForSchoolReasons = it.absenceForSchoolReasons, + lateness = it.lateness, + latenessExcused = it.latenessExcused, + exemption = it.exemption + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/CompletedLessonsMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/CompletedLessonsMapper.kt new file mode 100644 index 000000000..c42126eb7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/CompletedLessonsMapper.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.CompletedLesson +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.CompletedLesson as SdkCompletedLesson + +fun List.mapToEntities(semester: Semester) = map { + CompletedLesson( + studentId = semester.studentId, + diaryId = semester.diaryId, + date = it.date, + number = it.number, + subject = it.subject, + topic = it.topic, + teacher = it.teacher, + teacherSymbol = it.teacherSymbol, + substitution = it.substitution, + absence = it.absence, + resources = it.resources + ) +} 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 new file mode 100644 index 000000000..52dc9b30b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt @@ -0,0 +1,18 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.Conference as SdkConference + +fun List.mapToEntities(semester: Semester) = map { + Conference( + studentId = semester.studentId, + diaryId = semester.diaryId, + agenda = it.agenda, + conferenceId = it.id, + date = it.date, + presentOnConference = it.presentOnConference, + 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 new file mode 100644 index 000000000..bdb5efbba --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ExamMapper.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Exam +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.Exam as SdkExam + +fun List.mapToEntities(semester: Semester) = map { + Exam( + studentId = semester.studentId, + diaryId = semester.diaryId, + date = it.date, + entryDate = it.entryDate, + subject = it.subject, + group = it.group, + type = it.type, + description = it.description, + teacher = it.teacher, + teacherSymbol = it.teacherSymbol + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/GradeMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeMapper.kt new file mode 100644 index 000000000..178de682a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeMapper.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Grade +import io.github.wulkanowy.data.db.entities.GradeSummary +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.GradeSummary as SdkGradeSummary +import io.github.wulkanowy.sdk.pojo.Grade as SdkGrade + +fun List.mapToEntities(semester: Semester) = map { + Grade( + studentId = semester.studentId, + semesterId = semester.semesterId, + subject = it.subject, + entry = it.entry, + value = it.value, + modifier = it.modifier, + comment = it.comment, + color = it.color, + gradeSymbol = it.symbol, + description = it.description, + weight = it.weight, + weightValue = it.weightValue, + date = it.date, + teacher = it.teacher + ) +} + +@JvmName("mapGradeSummaryToEntities") +fun List.mapToEntities(semester: Semester) = map { + GradeSummary( + semesterId = semester.semesterId, + studentId = semester.studentId, + position = 0, + subject = it.name, + predictedGrade = it.predicted, + finalGrade = it.final, + pointsSum = it.pointsSum, + proposedPoints = it.proposedPoints, + finalPoints = it.finalPoints, + average = it.average + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/GradeStatisticsMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeStatisticsMapper.kt new file mode 100644 index 000000000..fbd40433c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/GradeStatisticsMapper.kt @@ -0,0 +1,80 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.GradePartialStatistics +import io.github.wulkanowy.data.db.entities.GradePointsStatistics +import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.pojos.GradeStatisticsItem +import io.github.wulkanowy.ui.modules.grade.statistics.ViewType +import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject as SdkGradeStatisticsSubject +import io.github.wulkanowy.sdk.pojo.GradeStatisticsSemester as SdkGradeStatisticsSemester +import io.github.wulkanowy.sdk.pojo.GradePointsStatistics as SdkGradePointsStatistics + +@JvmName("mapToEntitiesSubject") +fun List.mapToEntities(semester: Semester) = map { + GradePartialStatistics( + semesterId = semester.semesterId, + studentId = semester.studentId, + subject = it.subject, + classAverage = it.classAverage, + studentAverage = it.studentAverage, + classAmounts = it.classItems + .sortedBy { item -> item.grade } + .map { item -> item.amount }, + studentAmounts = it.studentItems.map { item -> item.amount } + ) +} + +@JvmName("mapToEntitiesSemester") +fun List.mapToEntities(semester: Semester) = map { + GradeSemesterStatistics( + semesterId = semester.semesterId, + studentId = semester.studentId, + subject = it.subject, + amounts = it.items + .sortedBy { item -> item.grade } + .map { item -> item.amount }, + studentGrade = it.items.singleOrNull { item -> item.isStudentHere }?.grade ?: 0 + ) +} + +@JvmName("mapToEntitiesPoints") +fun List.mapToEntities(semester: Semester) = map { + GradePointsStatistics( + semesterId = semester.semesterId, + studentId = semester.studentId, + subject = it.subject, + others = it.others, + student = it.student + ) +} + +fun List.mapPartialToStatisticItems() = filterNot { it.classAmounts.isEmpty() }.map { + GradeStatisticsItem( + type = ViewType.PARTIAL, + average = it.classAverage, + partial = it, + points = null, + semester = null + ) +} + +fun List.mapSemesterToStatisticItems() = filterNot { it.amounts.isEmpty() }.map { + GradeStatisticsItem( + type = ViewType.SEMESTER, + partial = null, + points = null, + average = "", + semester = it + ) +} + +fun List.mapPointsToStatisticsItems() = map { + GradeStatisticsItem( + type = ViewType.POINTS, + partial = null, + semester = null, + average = "", + points = it + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/HomeworkMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/HomeworkMapper.kt new file mode 100644 index 000000000..880a26d6d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/HomeworkMapper.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.sdk.pojo.Homework as SdkHomework +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.db.entities.Semester + +fun List.mapToEntities(semester: Semester) = map { + Homework( + semesterId = semester.semesterId, + studentId = semester.studentId, + date = it.date, + entryDate = it.entryDate, + subject = it.subject, + content = it.content, + teacher = it.teacher, + teacherSymbol = it.teacherSymbol, + attachments = it.attachments.map { attachment -> + attachment.url to attachment.name + } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/LuckyNumberMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/LuckyNumberMapper.kt new file mode 100644 index 000000000..78ebe1d6e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/LuckyNumberMapper.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Student +import java.time.LocalDate +import io.github.wulkanowy.sdk.pojo.LuckyNumber as SdkLuckyNumber + +fun SdkLuckyNumber.mapToEntity(student: Student) = LuckyNumber( + studentId = student.studentId, + date = LocalDate.now(), + luckyNumber = number +) 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 new file mode 100644 index 000000000..2c815b305 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt @@ -0,0 +1,53 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageAttachment +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient +import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment +import java.time.LocalDateTime +import io.github.wulkanowy.sdk.pojo.Message as SdkMessage + +fun List.mapToEntities(student: Student) = map { + Message( + studentId = student.id.toInt(), + realId = it.id ?: 0, + messageId = it.messageId ?: 0, + sender = it.sender?.name.orEmpty(), + senderId = it.sender?.loginId ?: 0, + recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów", + subject = it.subject.trim(), + date = it.date ?: LocalDateTime.now(), + folderId = it.folderId, + unread = it.unread ?: false, + removed = it.removed, + hasAttachments = it.hasAttachments + ).apply { + content = it.content.orEmpty() + unreadBy = it.unreadBy ?: 0 + readBy = it.readBy ?: 0 + } +} + +fun List.mapToEntities() = map { + MessageAttachment( + realId = it.id, + messageId = it.messageId, + oneDriveId = it.oneDriveId, + url = it.url, + filename = it.filename + ) +} + +fun List.mapFromEntities() = map { + SdkRecipient( + id = it.realId, + name = it.realName, + loginId = it.loginId, + reportingUnitId = it.unitId, + role = it.role, + hash = it.hash, + shortName = it.name + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt new file mode 100644 index 000000000..f0c375bfa --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.pojos.MobileDeviceToken +import io.github.wulkanowy.sdk.pojo.Token as SdkToken +import io.github.wulkanowy.sdk.pojo.Device as SdkDevice + +fun List.mapToEntities(semester: Semester) = map { + MobileDevice( + userLoginId = semester.studentId, + date = it.createDate, + deviceId = it.id, + name = it.name + ) +} + +fun SdkToken.mapToMobileDeviceToken() = MobileDeviceToken( + token = token, + symbol = symbol, + pin = pin, + qr = qrCodeImage +) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/NoteMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/NoteMapper.kt new file mode 100644 index 000000000..70941799b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/NoteMapper.kt @@ -0,0 +1,19 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.Note as SdkNote + +fun List.mapToEntities(semester: Semester) = map { + Note( + studentId = semester.studentId, + date = it.date, + teacher = it.teacher, + teacherSymbol = it.teacherSymbol, + category = it.category, + categoryType = it.categoryType.id, + isPointsShow = it.showPoints, + points = it.points, + content = it.content + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt new file mode 100644 index 000000000..9996f6800 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt @@ -0,0 +1,17 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient + +fun List.mapToEntities(userLoginId: Int) = map { + Recipient( + userLoginId = userLoginId, + realId = it.id, + realName = it.name, + name = it.shortName, + hash = it.hash, + loginId = it.loginId, + role = it.role, + unitId = it.reportingUnitId ?: 0 + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt new file mode 100644 index 000000000..71ea7099d --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.pojo.ReportingUnit as SdkReportingUnit + +fun List.mapToEntities(student: Student) = map { + ReportingUnit( + studentId = student.studentId, + unitId = it.id, + roles = it.roles, + senderId = it.senderId, + senderName = it.senderName, + shortName = it.short + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/SchoolInfoMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/SchoolInfoMapper.kt new file mode 100644 index 000000000..dc3a5a9e9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/SchoolInfoMapper.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.School +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.pojo.School as SdkSchool + +fun SdkSchool.mapToEntity(semester: Semester) = School( + studentId = semester.studentId, + classId = semester.classId, + name = name, + address = address, + contact = contact, + headmaster = headmaster, + pedagogue = pedagogue +) diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/SubjectMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/SubjectMapper.kt new file mode 100644 index 000000000..4dc95aaa7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/SubjectMapper.kt @@ -0,0 +1,14 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Subject +import io.github.wulkanowy.sdk.pojo.Subject as SdkSubject + +fun List.mapToEntities(semester: Semester) = map { + Subject( + studentId = semester.studentId, + diaryId = semester.diaryId, + name = it.name, + realId = it.id + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/TeacherMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/TeacherMapper.kt new file mode 100644 index 000000000..49cb7c294 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/TeacherMapper.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Teacher +import io.github.wulkanowy.sdk.pojo.Teacher as SdkTeacher + +fun List.mapToEntities(semester: Semester) = map { + Teacher( + studentId = semester.studentId, + name = it.name, + subject = it.subject, + shortName = it.short, + classId = semester.classId + ) +} 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 new file mode 100644 index 000000000..ffd2ae34e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt @@ -0,0 +1,41 @@ +package io.github.wulkanowy.data.mappers + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable +import io.github.wulkanowy.sdk.pojo.TimetableAdditional as SdkTimetableAdditional + +fun List.mapToEntities(semester: Semester) = map { + Timetable( + studentId = semester.studentId, + diaryId = semester.diaryId, + number = it.number, + start = it.start, + end = it.end, + date = it.date, + subject = it.subject, + subjectOld = it.subjectOld, + group = it.group, + room = it.room, + roomOld = it.roomOld, + teacher = it.teacher, + teacherOld = it.teacherOld, + info = it.info, + isStudentPlan = it.studentPlan, + changes = it.changes, + canceled = it.canceled + ) +} + +@JvmName("mapToEntitiesTimetableAdditional") +fun List.mapToEntities(semester: Semester) = map { + TimetableAdditional( + studentId = semester.studentId, + diaryId = semester.diaryId, + subject = it.subject, + date = it.date, + start = it.start, + end = it.end + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt index 34b62abdb..88257470d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt @@ -1,14 +1,19 @@ package io.github.wulkanowy.data.pojos +import io.github.wulkanowy.data.db.entities.GradePartialStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics -import io.github.wulkanowy.data.db.entities.GradeStatistics +import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics import io.github.wulkanowy.ui.modules.grade.statistics.ViewType data class GradeStatisticsItem( val type: ViewType, - val partial: List, + val average: String, + + val partial: GradePartialStatistics?, + + val semester: GradeSemesterStatistics?, val points: GradePointsStatistics? ) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt similarity index 93% rename from app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt rename to app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt index ff538969b..aea8632ac 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AppCreatorRepository.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.data.repositories.appcreator +package io.github.wulkanowy.data.repositories import android.content.res.AssetManager import com.squareup.moshi.Moshi 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 new file mode 100644 index 000000000..9a6528f39 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt @@ -0,0 +1,57 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.AttendanceDao +import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.Absent +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.uniqueSubtract +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.LocalTime +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AttendanceRepository @Inject constructor( + private val attendanceDb: AttendanceDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val cacheKey = "attendance" + + fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) }, + query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getAttendance(start.monday, end.sunday, semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + attendanceDb.deleteAll(old uniqueSubtract new) + attendanceDb.insertAll(new uniqueSubtract old) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) + }, + filterResult = { it.filter { item -> item.date in start..end } } + ) + + suspend fun excuseForAbsence(student: Student, semester: Semester, absenceList: List, reason: String? = null) { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance -> + Absent( + date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)), + timeId = attendance.timeId + ) + }, reason) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt new file mode 100644 index 000000000..4edb507be --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.uniqueSubtract +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AttendanceSummaryRepository @Inject constructor( + private val attendanceDb: AttendanceSummaryDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val cacheKey = "attendance_summary" + + fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) }, + query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getAttendanceSummary(subjectId) + .mapToEntities(semester, subjectId) + }, + saveFetchResult = { old, new -> + attendanceDb.deleteAll(old uniqueSubtract new) + attendanceDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) + } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt new file mode 100644 index 000000000..59aabdd54 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.CompletedLessonsDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.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.monday +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.uniqueSubtract +import java.time.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CompletedLessonsRepository @Inject constructor( + private val completedLessonsDb: CompletedLessonsDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val cacheKey = "completed" + + fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) }, + query = { completedLessonsDb.loadAll(semester.studentId, semester.diaryId, start.monday, end.sunday) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getCompletedLessons(start.monday, end.sunday) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + completedLessonsDb.deleteAll(old uniqueSubtract new) + completedLessonsDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) + }, + filterResult = { it.filter { item -> item.date in start..end } } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt new file mode 100644 index 000000000..befcf9e6b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt @@ -0,0 +1,39 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.ConferenceDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.uniqueSubtract +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ConferenceRepository @Inject constructor( + private val conferenceDb: ConferenceDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val cacheKey = "conference" + + fun getConferences(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) }, + query = { conferenceDb.loadAll(semester.diaryId, student.studentId) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getConferences() + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + conferenceDb.deleteAll(old uniqueSubtract new) + conferenceDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) + } + ) +} 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 new file mode 100644 index 000000000..bd6e7d2d6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.ExamDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.endExamsDay +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.startExamsDay +import io.github.wulkanowy.utils.uniqueSubtract +import java.time.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ExamRepository @Inject constructor( + private val examDb: ExamDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val cacheKey = "exam" + + fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) }, + query = { examDb.loadAll(semester.diaryId, semester.studentId, start.startExamsDay, start.endExamsDay) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getExams(start.startExamsDay, start.endExamsDay, semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + examDb.deleteAll(old uniqueSubtract new) + examDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) + }, + filterResult = { it.filter { item -> item.date in start..end } } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt similarity index 54% rename from app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt rename to app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt index 8cbbfdfb0..bab290f32 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt @@ -1,9 +1,16 @@ -package io.github.wulkanowy.data.repositories.grade +package io.github.wulkanowy.data.repositories +import io.github.wulkanowy.data.db.dao.GradeDao +import io.github.wulkanowy.data.db.dao.GradeSummaryDao import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow @@ -15,24 +22,40 @@ import javax.inject.Singleton @Singleton class GradeRepository @Inject constructor( - private val local: GradeLocal, - private val remote: GradeRemote + private val gradeDb: GradeDao, + private val gradeSummaryDb: GradeSummaryDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, ) { + private val cacheKey = "grade" + fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource( - shouldFetch = { (details, summaries) -> details.isEmpty() || summaries.isEmpty() || forceRefresh }, - query = { local.getGradesDetails(semester).combine(local.getGradesSummary(semester)) { details, summaries -> details to summaries } }, - fetch = { remote.getGrades(student, semester) }, - saveFetchResult = { old, new -> - refreshGradeDetails(student, old.first, new.first, notify) - refreshGradeSummaries(old.second, new.second, notify) + shouldFetch = { (details, summaries) -> details.isEmpty() || summaries.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) }, + query = { + gradeDb.loadAll(semester.semesterId, semester.studentId).combine(gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)) { details, summaries -> + details to summaries + } + }, + fetch = { + val (details, summary) = sdk.init(student) + .switchDiary(semester.diaryId, semester.schoolYear) + .getGrades(semester.semesterId) + + details.mapToEntities(semester) to summary.mapToEntities(semester) + }, + saveFetchResult = { (oldDetails, oldSummary), (newDetails, newSummary) -> + refreshGradeDetails(student, oldDetails, newDetails, notify) + refreshGradeSummaries(oldSummary, newSummary, notify) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) } ) private suspend fun refreshGradeDetails(student: Student, oldGrades: List, newDetails: List, notify: Boolean) { val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date ?: student.registrationDate.toLocalDate() - local.deleteGrades(oldGrades uniqueSubtract newDetails) - local.saveGrades((newDetails uniqueSubtract oldGrades).onEach { + gradeDb.deleteAll(oldGrades uniqueSubtract newDetails) + gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach { if (it.date >= notifyBreakDate) it.apply { isRead = false if (notify) isNotified = false @@ -41,8 +64,8 @@ class GradeRepository @Inject constructor( } private suspend fun refreshGradeSummaries(oldSummaries: List, newSummary: List, notify: Boolean) { - local.deleteGradesSummary(oldSummaries uniqueSubtract newSummary) - local.saveGradesSummary((newSummary uniqueSubtract oldSummaries).onEach { summary -> + gradeSummaryDb.deleteAll(oldSummaries uniqueSubtract newSummary) + gradeSummaryDb.insertAll((newSummary uniqueSubtract oldSummaries).onEach { summary -> val oldSummary = oldSummaries.find { oldSummary -> oldSummary.subject == summary.subject } summary.isPredictedGradeNotified = when { summary.predictedGrade.isEmpty() -> true @@ -69,30 +92,30 @@ class GradeRepository @Inject constructor( } fun getUnreadGrades(semester: Semester): Flow> { - return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isRead } } + return gradeDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { grade -> !grade.isRead } } } fun getNotNotifiedGrades(semester: Semester): Flow> { - return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isNotified } } + return gradeDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { grade -> !grade.isNotified } } } fun getNotNotifiedPredictedGrades(semester: Semester): Flow> { - return local.getGradesSummary(semester).map { it.filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified } } + return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified } } } fun getNotNotifiedFinalGrades(semester: Semester): Flow> { - return local.getGradesSummary(semester).map { it.filter { gradeSummary -> !gradeSummary.isFinalGradeNotified } } + return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId).map { it.filter { gradeSummary -> !gradeSummary.isFinalGradeNotified } } } suspend fun updateGrade(grade: Grade) { - return local.updateGrades(listOf(grade)) + return gradeDb.updateAll(listOf(grade)) } suspend fun updateGrades(grades: List) { - return local.updateGrades(grades) + return gradeDb.updateAll(grades) } suspend fun updateGradesSummary(gradesSummary: List) { - return local.updateGradesSummary(gradesSummary) + return gradeSummaryDb.updateAll(gradesSummary) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt new file mode 100644 index 000000000..ab65fb141 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt @@ -0,0 +1,144 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao +import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao +import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao +import io.github.wulkanowy.data.db.entities.GradePartialStatistics +import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapPartialToStatisticItems +import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems +import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.uniqueSubtract +import java.util.Locale +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GradeStatisticsRepository @Inject constructor( + private val gradePartialStatisticsDb: GradePartialStatisticsDao, + private val gradePointsStatisticsDb: GradePointsStatisticsDao, + private val gradeSemesterStatisticsDb: GradeSemesterStatisticsDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val partialCacheKey = "grade_stats_partial" + private val semesterCacheKey = "grade_stats_semester" + private val pointsCacheKey = "grade_stats_points" + + fun getGradesPartialStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(partialCacheKey, semester)) }, + query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getGradesPartialStatistics(semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + gradePartialStatisticsDb.deleteAll(old uniqueSubtract new) + gradePartialStatisticsDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(partialCacheKey, semester)) + }, + mapResult = { items -> + when (subjectName) { + "Wszystkie" -> { + val numerator = items.map { + it.classAverage.replace(",", ".").toDoubleOrNull() ?: .0 + }.filterNot { it == .0 } + (items.reversed() + GradePartialStatistics( + studentId = semester.studentId, + semesterId = semester.semesterId, + subject = subjectName, + classAverage = if (numerator.isEmpty()) "" else numerator.average().let { + "%.2f".format(Locale.FRANCE, it) + }, + studentAverage = "", + classAmounts = items.map { it.classAmounts }.sumGradeAmounts(), + studentAmounts = items.map { it.studentAmounts }.sumGradeAmounts() + )).reversed() + } + else -> items.filter { it.subject == subjectName } + }.mapPartialToStatisticItems() + } + ) + + fun getGradesSemesterStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(semesterCacheKey, semester)) }, + query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getGradesSemesterStatistics(semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + gradeSemesterStatisticsDb.deleteAll(old uniqueSubtract new) + gradeSemesterStatisticsDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(semesterCacheKey, semester)) + }, + mapResult = { items -> + val itemsWithAverage = items.map { item -> + item.copy().apply { + val denominator = item.amounts.sum() + average = if (denominator == 0) "" else (item.amounts.mapIndexed { gradeValue, amount -> + (gradeValue + 1) * amount + }.sum().toDouble() / denominator).let { + "%.2f".format(Locale.FRANCE, it) + } + } + } + when (subjectName) { + "Wszystkie" -> (itemsWithAverage.reversed() + GradeSemesterStatistics( + studentId = semester.studentId, + semesterId = semester.semesterId, + subject = subjectName, + amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(), + studentGrade = 0 + ).apply { + average = itemsWithAverage.mapNotNull { it.average.replace(",", ".").toDoubleOrNull() }.average().let { + "%.2f".format(Locale.FRANCE, it) + } + }).reversed() + else -> itemsWithAverage.filter { it.subject == subjectName } + }.mapSemesterToStatisticItems() + } + ) + + fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(pointsCacheKey, semester)) }, + query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getGradesPointsStatistics(semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + gradePointsStatisticsDb.deleteAll(old uniqueSubtract new) + gradePointsStatisticsDb.insertAll(new uniqueSubtract old) + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(pointsCacheKey, semester)) + }, + mapResult = { items -> + when (subjectName) { + "Wszystkie" -> items + else -> items.filter { it.subject == subjectName } + }.mapPointsToStatisticsItems() + } + ) + + private fun List>.sumGradeAmounts(): List { + val result = mutableListOf(0, 0, 0, 0, 0, 0) + forEach { + it.forEachIndexed { grade, amount -> + result[grade] += amount + } + } + return result + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt new file mode 100644 index 000000000..7625dbbc8 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.HomeworkDao +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.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.monday +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.uniqueSubtract +import java.time.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class HomeworkRepository @Inject constructor( + private val homeworkDb: HomeworkDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val cacheKey = "homework" + + fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) }, + query = { homeworkDb.loadAll(semester.semesterId, semester.studentId, start.monday, end.sunday) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getHomework(start.monday, end.sunday) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + homeworkDb.deleteAll(old uniqueSubtract new) + homeworkDb.insertAll(new uniqueSubtract old) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) + } + ) + + suspend fun toggleDone(homework: Homework) { + homeworkDb.updateAll(listOf(homework.apply { + isDone = !isDone + })) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/logger/LoggerRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/LoggerRepository.kt similarity index 96% rename from app/src/main/java/io/github/wulkanowy/data/repositories/logger/LoggerRepository.kt rename to app/src/main/java/io/github/wulkanowy/data/repositories/LoggerRepository.kt index 85168fee5..6d509b026 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/logger/LoggerRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/LoggerRepository.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.data.repositories.logger +package io.github.wulkanowy.data.repositories import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext 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 new file mode 100644 index 000000000..801292b42 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt @@ -0,0 +1,41 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.LuckyNumberDao +import io.github.wulkanowy.data.db.entities.LuckyNumber +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntity +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import java.time.LocalDate.now +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class LuckyNumberRepository @Inject constructor( + private val luckyNumberDb: LuckyNumberDao, + private val sdk: Sdk +) { + + fun getLuckyNumber(student: Student, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource( + shouldFetch = { it == null || forceRefresh }, + query = { luckyNumberDb.load(student.studentId, now()) }, + fetch = { sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student) }, + saveFetchResult = { old, new -> + if (new != old) { + old?.let { luckyNumberDb.deleteAll(listOfNotNull(it)) } + luckyNumberDb.insertAll(listOfNotNull((new?.apply { + if (notify) isNotified = false + }))) + } + } + ) + + suspend fun getNotNotifiedLuckyNumber(student: Student) = luckyNumberDb.load(student.studentId, now()).map { + if (it?.isNotified == false) it else null + }.first() + + suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) = luckyNumberDb.updateAll(listOfNotNull(luckyNumber)) +} 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 new file mode 100644 index 000000000..ea7b2b0e0 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt @@ -0,0 +1,102 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.MessageAttachmentDao +import io.github.wulkanowy.data.db.dao.MessagesDao +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED +import io.github.wulkanowy.data.mappers.mapFromEntities +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.Folder +import io.github.wulkanowy.sdk.pojo.SentMessage +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.uniqueSubtract +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import timber.log.Timber +import java.time.LocalDateTime.now +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MessageRepository @Inject constructor( + private val messagesDb: MessagesDao, + private val messageAttachmentDao: MessageAttachmentDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val cacheKey = "message" + + @Suppress("UNUSED_PARAMETER") + fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student, folder)) }, + query = { messagesDb.loadAll(student.id.toInt(), folder.id) }, + fetch = { sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now()).mapToEntities(student) }, + saveFetchResult = { old, new -> + messagesDb.deleteAll(old uniqueSubtract new) + messagesDb.insertAll((new uniqueSubtract old).onEach { + it.isNotified = !notify + }) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder)) + } + ) + + fun getMessage(student: Student, message: Message, markAsRead: Boolean = false) = networkBoundResource( + shouldFetch = { + checkNotNull(it, { "This message no longer exist!" }) + Timber.d("Message content in db empty: ${it.message.content.isEmpty()}") + it.message.unread || it.message.content.isEmpty() + }, + query = { messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) }, + fetch = { + sdk.init(student).getMessageDetails(it!!.message.messageId, message.folderId, markAsRead, message.realId).let { details -> + details.content to details.attachments.mapToEntities() + } + }, + saveFetchResult = { old, (downloadedMessage, attachments) -> + checkNotNull(old, { "Fetched message no longer exist!" }) + messagesDb.updateAll(listOf(old.message.copy(unread = !markAsRead).apply { + id = old.message.id + content = content.ifBlank { downloadedMessage } + })) + messageAttachmentDao.insertAttachments(attachments) + Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read") + } + ) + + fun getNotNotifiedMessages(student: Student): Flow> { + return messagesDb.loadAll(student.id.toInt(), RECEIVED.id).map { it.filter { message -> !message.isNotified && message.unread } } + } + + suspend fun updateMessages(messages: List) { + return messagesDb.updateAll(messages) + } + + suspend fun sendMessage(student: Student, subject: String, content: String, recipients: List): SentMessage { + return sdk.init(student).sendMessage( + subject = subject, + content = content, + recipients = recipients.mapFromEntities() + ) + } + + suspend fun deleteMessage(student: Student, message: Message) { + val isDeleted = sdk.init(student).deleteMessages(listOf(message.messageId), message.folderId) + + if (message.folderId != MessageFolder.TRASHED.id) { + if (isDeleted) messagesDb.updateAll(listOf(message.copy(folderId = MessageFolder.TRASHED.id).apply { + id = message.id + content = message.content + })) + } else messagesDb.deleteAll(listOf(message)) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt new file mode 100644 index 000000000..7e83ef7d5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt @@ -0,0 +1,56 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.MobileDeviceDao +import io.github.wulkanowy.data.db.entities.MobileDevice +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken +import io.github.wulkanowy.data.pojos.MobileDeviceToken +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.uniqueSubtract +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class MobileDeviceRepository @Inject constructor( + private val mobileDb: MobileDeviceDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val cacheKey = "devices" + + fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student)) }, + query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getRegisteredDevices() + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + mobileDb.deleteAll(old uniqueSubtract new) + mobileDb.insertAll(new uniqueSubtract old) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student)) + } + ) + + suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .unregisterDevice(device.deviceId) + + mobileDb.deleteAll(listOf(device)) + } + + suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getToken() + .mapToMobileDeviceToken() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt new file mode 100644 index 000000000..85789f098 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt @@ -0,0 +1,60 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.NoteDao +import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.uniqueSubtract +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class NoteRepository @Inject constructor( + private val noteDb: NoteDao, + private val sdk: Sdk, + private val refreshHelper: AutoRefreshHelper, +) { + + private val cacheKey = "note" + + fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) }, + query = { noteDb.loadAll(student.studentId) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getNotes(semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + noteDb.deleteAll(old uniqueSubtract new) + noteDb.insertAll((new uniqueSubtract old).onEach { + if (it.date >= student.registrationDate.toLocalDate()) it.apply { + isRead = false + if (notify) isNotified = false + } + }) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester)) + } + ) + + fun getNotNotifiedNotes(student: Student): Flow> { + return noteDb.loadAll(student.studentId).map { it.filter { note -> !note.isNotified } } + } + + suspend fun updateNote(note: Note) { + noteDb.updateAll(listOf(note)) + } + + suspend fun updateNotes(notes: List) { + noteDb.updateAll(notes) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt similarity index 98% rename from app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt rename to app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt index da31751a1..8cb815cc3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.data.repositories.preferences +package io.github.wulkanowy.data.repositories import android.content.Context import android.content.SharedPreferences 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 new file mode 100644 index 000000000..24ab5f0c9 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt @@ -0,0 +1,40 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.RecipientDao +import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class RecipientRepository @Inject constructor( + private val recipientDb: RecipientDao, + private val sdk: Sdk +) { + + suspend fun refreshRecipients(student: Student, unit: ReportingUnit, role: Int) { + val new = sdk.init(student).getRecipients(unit.unitId, role).mapToEntities(unit.senderId) + val old = recipientDb.loadAll(unit.senderId, unit.unitId, role) + + recipientDb.deleteAll(old uniqueSubtract new) + recipientDb.insertAll(new uniqueSubtract old) + } + + suspend fun getRecipients(student: Student, unit: ReportingUnit, role: Int): List { + return recipientDb.loadAll(unit.senderId, unit.unitId, role).ifEmpty { + refreshRecipients(student, unit, role) + + recipientDb.loadAll(unit.senderId, unit.unitId, role) + } + } + + suspend fun getMessageRecipients(student: Student, message: Message): List { + return sdk.init(student).getMessageRecipients(message.messageId, message.senderId).mapToEntities(student.userLoginId) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recover/RecoverRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt similarity index 79% rename from app/src/main/java/io/github/wulkanowy/data/repositories/recover/RecoverRemote.kt rename to app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt index 11eac71e0..5e1063558 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recover/RecoverRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt @@ -1,11 +1,11 @@ -package io.github.wulkanowy.data.repositories.recover +package io.github.wulkanowy.data.repositories import io.github.wulkanowy.sdk.Sdk import javax.inject.Inject import javax.inject.Singleton @Singleton -class RecoverRemote @Inject constructor(private val sdk: Sdk) { +class RecoverRepository @Inject constructor(private val sdk: Sdk) { suspend fun getReCaptchaSiteKey(host: String, symbol: String): Pair { return sdk.getPasswordResetCaptchaCode(host, symbol) @@ -15,4 +15,3 @@ class RecoverRemote @Inject constructor(private val sdk: Sdk) { return sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse) } } - diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt new file mode 100644 index 000000000..792e66b5e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.ReportingUnitDao +import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.uniqueSubtract +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ReportingUnitRepository @Inject constructor( + private val reportingUnitDb: ReportingUnitDao, + private val sdk: Sdk +) { + + suspend fun refreshReportingUnits(student: Student) { + val new = sdk.init(student).getReportingUnits().mapToEntities(student) + val old = reportingUnitDb.load(student.studentId) + + reportingUnitDb.deleteAll(old.uniqueSubtract(new)) + reportingUnitDb.insertAll(new.uniqueSubtract(old)) + } + + suspend fun getReportingUnits(student: Student): List { + return reportingUnitDb.load(student.studentId).ifEmpty { + refreshReportingUnits(student) + + reportingUnitDb.load(student.studentId) + } + } + + suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? { + return reportingUnitDb.loadOne(student.studentId, unitId) ?: run { + refreshReportingUnits(student) + + return reportingUnitDb.loadOne(student.studentId, unitId) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt new file mode 100644 index 000000000..36d5c974a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt @@ -0,0 +1,31 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.SchoolDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntity +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SchoolRepository @Inject constructor( + private val schoolDb: SchoolDao, + private val sdk: Sdk +) { + + fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it == null || forceRefresh }, + query = { schoolDb.load(semester.studentId, semester.classId) }, + fetch = { sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool().mapToEntity(semester) }, + saveFetchResult = { old, new -> + if (new != old && old != null) { + schoolDb.deleteAll(listOf(old)) + schoolDb.insertAll(listOf(new)) + } + schoolDb.insertAll(listOf(new)) + } + ) +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt similarity index 73% rename from app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt rename to app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt index 2748f1df5..8942391c8 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt @@ -1,10 +1,13 @@ -package io.github.wulkanowy.data.repositories.semester +package io.github.wulkanowy.data.repositories +import io.github.wulkanowy.data.db.dao.SemesterDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.DispatchersProvider import io.github.wulkanowy.utils.getCurrentOrLast +import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.isCurrent import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.withContext @@ -14,17 +17,17 @@ import javax.inject.Singleton @Singleton class SemesterRepository @Inject constructor( - private val remote: SemesterRemote, - private val local: SemesterLocal, + private val semesterDb: SemesterDao, + private val sdk: Sdk, private val dispatchers: DispatchersProvider ) { suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false) = withContext(dispatchers.backgroundThread) { - val semesters = local.getSemesters(student) + val semesters = semesterDb.loadAll(student.studentId, student.classId) if (isShouldFetch(student, semesters, forceRefresh, refreshOnNoCurrent)) { refreshSemesters(student) - local.getSemesters(student) + semesterDb.loadAll(student.studentId, student.classId) } else semesters } @@ -41,12 +44,12 @@ class SemesterRepository @Inject constructor( } private suspend fun refreshSemesters(student: Student) { - val new = remote.getSemesters(student) + val new = sdk.init(student).getSemesters().mapToEntities(student.studentId) if (new.isEmpty()) return Timber.i("Empty semester list!") - val old = local.getSemesters(student) - local.deleteSemesters(old.uniqueSubtract(new)) - local.saveSemesters(new.uniqueSubtract(old)) + val old = semesterDb.loadAll(student.studentId, student.classId) + semesterDb.deleteAll(old.uniqueSubtract(new)) + semesterDb.insertSemesters(new.uniqueSubtract(old)) } suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = withContext(dispatchers.backgroundThread) { 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 new file mode 100644 index 000000000..5b80035ba --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt @@ -0,0 +1,85 @@ +package io.github.wulkanowy.data.repositories + +import android.content.Context +import dagger.hilt.android.qualifiers.ApplicationContext +import io.github.wulkanowy.data.db.dao.SemesterDao +import io.github.wulkanowy.data.db.dao.StudentDao +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.StudentWithSemesters +import io.github.wulkanowy.data.exceptions.NoCurrentStudentException +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.DispatchersProvider +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 +) { + + suspend fun isStudentSaved(): Boolean = getSavedStudents(false).isNotEmpty() + + suspend fun isCurrentStudentSet(): Boolean = studentDb.loadCurrent()?.isCurrent ?: false + + suspend fun getStudentsApi(pin: String, symbol: String, token: String): List { + return sdk.getStudentsFromMobileApi(token, pin, symbol, "").mapToEntities() + } + + suspend fun getStudentsScrapper(email: String, password: String, scrapperBaseUrl: String, symbol: String): List { + return sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol).mapToEntities(password) + } + + suspend fun getStudentsHybrid(email: String, password: String, scrapperBaseUrl: String, symbol: String): List { + return sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol).mapToEntities(password) + } + + suspend fun getSavedStudents(decryptPass: Boolean = true) = withContext(dispatchers.backgroundThread) { + studentDb.loadStudentsWithSemesters().map { + it.apply { + if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) student.password = decrypt(student.password) + } + } + } + + suspend fun getStudentById(id: Int) = withContext(dispatchers.backgroundThread) { + studentDb.loadById(id)?.apply { + if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password) + } + } ?: throw NoCurrentStudentException() + + suspend fun getCurrentStudent(decryptPass: Boolean = true) = withContext(dispatchers.backgroundThread) { + studentDb.loadCurrent()?.apply { + if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password) + } + } ?: throw NoCurrentStudentException() + + suspend fun saveStudents(studentsWithSemesters: List): List { + semesterDb.insertSemesters(studentsWithSemesters.flatMap { it.semesters }) + + return withContext(dispatchers.backgroundThread) { + studentDb.insertAll(studentsWithSemesters.map { it.student }.map { + if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) it.copy(password = encrypt(it.password, context)) + else it + }) + } + } + + suspend fun switchStudent(studentWithSemesters: StudentWithSemesters) { + with(studentDb) { + resetCurrent() + updateCurrent(studentWithSemesters.student.id) + } + } + + suspend fun logoutStudent(student: Student) { + studentDb.delete(student) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt new file mode 100644 index 000000000..ef07a1d47 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.SubjectDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.uniqueSubtract +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class SubjectRepository @Inject constructor( + private val subjectDao: SubjectDao, + private val sdk: Sdk +) { + + fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh }, + query = { subjectDao.loadAll(semester.diaryId, semester.studentId) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getSubjects().mapToEntities(semester) + }, + saveFetchResult = { old, new -> + subjectDao.deleteAll(old uniqueSubtract new) + subjectDao.insertAll(new uniqueSubtract old) + } + ) +} 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 new file mode 100644 index 000000000..25da718ca --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.TeacherDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.uniqueSubtract +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TeacherRepository @Inject constructor( + private val teacherDb: TeacherDao, + private val sdk: Sdk +) { + + fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource( + shouldFetch = { it.isEmpty() || forceRefresh }, + query = { teacherDb.loadAll(semester.studentId, semester.classId) }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getTeachers(semester.semesterId) + .mapToEntities(semester) + }, + saveFetchResult = { old, new -> + teacherDb.deleteAll(old uniqueSubtract new) + teacherDb.insertAll(new uniqueSubtract old) + } + ) +} 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 new file mode 100644 index 000000000..fa1898f5b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt @@ -0,0 +1,84 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao +import io.github.wulkanowy.data.db.dao.TimetableDao +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper +import io.github.wulkanowy.utils.AutoRefreshHelper +import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.networkBoundResource +import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.uniqueSubtract +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import java.time.LocalDate +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TimetableRepository @Inject constructor( + private val timetableDb: TimetableDao, + private val timetableAdditionalDb: TimetableAdditionalDao, + private val sdk: Sdk, + private val schedulerHelper: TimetableNotificationSchedulerHelper, + private val refreshHelper: AutoRefreshHelper, +) { + + private val cacheKey = "timetable" + + fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean, refreshAdditional: Boolean = false) = networkBoundResource( + shouldFetch = { (timetable, additional) -> timetable.isEmpty() || (additional.isEmpty() && refreshAdditional) || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) }, + query = { + timetableDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) + .map { schedulerHelper.scheduleNotifications(it, student); it } + .combine(timetableAdditionalDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)) { timetable, additional -> + timetable to additional + } + }, + fetch = { + sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getTimetable(start.monday, end.sunday) + .let { (normal, additional) -> normal.mapToEntities(semester) to additional.mapToEntities(semester) } + + }, + saveFetchResult = { (oldTimetable, oldAdditional), (newTimetable, newAdditional) -> + refreshTimetable(student, oldTimetable, newTimetable) + refreshAdditional(oldAdditional, newAdditional) + + refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end)) + }, + filterResult = { (timetable, additional) -> + timetable.filter { item -> + item.date in start..end + } to additional.filter { item -> + item.date in start..end + } + } + ) + + private suspend fun refreshTimetable(student: Student, old: List, new: List) { + timetableDb.deleteAll(old.uniqueSubtract(new).also { schedulerHelper.cancelScheduled(it) }) + timetableDb.insertAll(new.uniqueSubtract(old).also { schedulerHelper.scheduleNotifications(it, student) }.map { item -> + item.also { new -> + old.singleOrNull { new.start == it.start }?.let { old -> + return@map new.copy( + room = if (new.room.isEmpty()) old.room else new.room, + teacher = if (new.teacher.isEmpty() && !new.changes && !old.changes) old.teacher else new.teacher + ) + } + } + }) + } + + private suspend fun refreshAdditional(old: List, new: List) { + timetableAdditionalDb.deleteAll(old.uniqueSubtract(new)) + timetableAdditionalDb.insertAll(new.uniqueSubtract(old)) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt deleted file mode 100644 index 9aaa52304..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -import io.github.wulkanowy.data.db.dao.AttendanceDao -import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.Flow -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) { - - suspend fun saveAttendance(attendance: List) { - attendanceDb.insertAll(attendance) - } - - suspend fun deleteAttendance(attendance: List) { - attendanceDb.deleteAll(attendance) - } - - fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow> { - return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt deleted file mode 100644 index 870690ec0..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -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.sdk.Sdk -import io.github.wulkanowy.sdk.pojo.Absent -import io.github.wulkanowy.utils.init -import java.time.LocalDate -import java.time.LocalDateTime -import java.time.LocalTime -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getAttendance(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getAttendance(startDate, endDate, semester.semesterId) - .map { - Attendance( - studentId = semester.studentId, - diaryId = semester.diaryId, - date = it.date, - timeId = it.timeId, - number = it.number, - subject = it.subject, - name = it.name, - presence = it.presence, - absence = it.absence, - exemption = it.exemption, - lateness = it.lateness, - excused = it.excused, - deleted = it.deleted, - excusable = it.excusable, - excuseStatus = it.excuseStatus?.name - ) - } - } - - suspend fun excuseAbsence(student: Student, semester: Semester, absenceList: List, reason: String?): Boolean { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance -> - Absent( - date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)), - timeId = attendance.timeId - ) - }, reason) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt deleted file mode 100644 index 60f864f27..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendance - -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.utils.monday -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.sunday -import io.github.wulkanowy.utils.uniqueSubtract -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceRepository @Inject constructor( - private val local: AttendanceLocal, - private val remote: AttendanceRemote -) { - - fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getAttendance(semester, start.monday, end.sunday) }, - fetch = { remote.getAttendance(student, semester, start.monday, end.sunday) }, - saveFetchResult = { old, new -> - local.deleteAttendance(old uniqueSubtract new) - local.saveAttendance(new uniqueSubtract old) - }, - filterResult = { it.filter { item -> item.date in start..end } } - ) - - suspend fun excuseForAbsence(student: Student, semester: Semester, attendanceList: List, reason: String? = null) { - remote.excuseAbsence(student, semester, attendanceList, reason) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt deleted file mode 100644 index 703bc9474..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendancesummary - -import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: AttendanceSummaryDao) { - - suspend fun saveAttendanceSummary(attendance: List) { - attendanceDb.insertAll(attendance) - } - - suspend fun deleteAttendanceSummary(attendance: List) { - attendanceDb.deleteAll(attendance) - } - - fun getAttendanceSummary(semester: Semester, subjectId: Int): Flow> { - return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt deleted file mode 100644 index 29a0b9a7b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendancesummary - -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceSummaryRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getAttendanceSummary(subjectId) - .map { - AttendanceSummary( - studentId = semester.studentId, - diaryId = semester.diaryId, - subjectId = subjectId, - month = it.month, - presence = it.presence, - absence = it.absence, - absenceExcused = it.absenceExcused, - absenceForSchoolReasons = it.absenceForSchoolReasons, - lateness = it.lateness, - latenessExcused = it.latenessExcused, - exemption = it.exemption - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt deleted file mode 100644 index 5dbe1ab05..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRepository.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.attendancesummary - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.uniqueSubtract -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class AttendanceSummaryRepository @Inject constructor( - private val local: AttendanceSummaryLocal, - private val remote: AttendanceSummaryRemote -) { - - fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getAttendanceSummary(semester, subjectId) }, - fetch = { remote.getAttendanceSummary(student, semester, subjectId) }, - saveFetchResult = { old, new -> - local.deleteAttendanceSummary(old uniqueSubtract new) - local.saveAttendanceSummary(new uniqueSubtract old) - } - ) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt deleted file mode 100644 index 51a1bdbfc..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import io.github.wulkanowy.data.db.dao.CompletedLessonsDao -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.Flow -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class CompletedLessonsLocal @Inject constructor(private val completedLessonsDb: CompletedLessonsDao) { - - suspend fun saveCompletedLessons(completedLessons: List) { - completedLessonsDb.insertAll(completedLessons) - } - - suspend fun deleteCompleteLessons(completedLessons: List) { - completedLessonsDb.deleteAll(completedLessons) - } - - fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): Flow> { - return completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt deleted file mode 100644 index d15a27623..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class CompletedLessonsRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getCompletedLessons(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getCompletedLessons(startDate, endDate) - .map { - it.absence - CompletedLesson( - studentId = semester.studentId, - diaryId = semester.diaryId, - date = it.date, - number = it.number, - subject = it.subject, - topic = it.topic, - teacher = it.teacher, - teacherSymbol = it.teacherSymbol, - substitution = it.substitution, - absence = it.absence, - resources = it.resources - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt deleted file mode 100644 index 61268a66a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRepository.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.completedlessons - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.monday -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.sunday -import io.github.wulkanowy.utils.uniqueSubtract -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class CompletedLessonsRepository @Inject constructor( - private val local: CompletedLessonsLocal, - private val remote: CompletedLessonsRemote -) { - - fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getCompletedLessons(semester, start.monday, end.sunday) }, - fetch = { remote.getCompletedLessons(student, semester, start.monday, end.sunday) }, - saveFetchResult = { old, new -> - local.deleteCompleteLessons(old uniqueSubtract new) - local.saveCompletedLessons(new uniqueSubtract old) - }, - filterResult = { it.filter { item -> item.date in start..end } } - ) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt deleted file mode 100644 index acc55b5ec..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import io.github.wulkanowy.data.db.dao.ExamDao -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.Flow -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ExamLocal @Inject constructor(private val examDb: ExamDao) { - - fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow> { - return examDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate) - } - - suspend fun saveExams(exams: List) { - examDb.insertAll(exams) - } - - suspend fun deleteExams(exams: List) { - examDb.deleteAll(exams) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt deleted file mode 100644 index ac4aa93d7..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ExamRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getExams(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getExams(startDate, endDate, semester.semesterId) - .map { - Exam( - studentId = semester.studentId, - diaryId = semester.diaryId, - date = it.date, - entryDate = it.entryDate, - subject = it.subject, - group = it.group, - type = it.type, - description = it.description, - teacher = it.teacher, - teacherSymbol = it.teacherSymbol - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt deleted file mode 100644 index 3f4591a25..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.exam - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.endExamsDay -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.startExamsDay -import io.github.wulkanowy.utils.uniqueSubtract -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ExamRepository @Inject constructor( - private val local: ExamLocal, - private val remote: ExamRemote -) { - - fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getExams(semester, start.startExamsDay, start.endExamsDay) }, - fetch = { remote.getExams(student, semester, start.startExamsDay, start.endExamsDay) }, - saveFetchResult = { old, new -> - local.deleteExams(old uniqueSubtract new) - local.saveExams(new uniqueSubtract old) - }, - filterResult = { it.filter { item -> item.date in start..end } } - ) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt deleted file mode 100644 index ed3635423..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeLocal.kt +++ /dev/null @@ -1,49 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import io.github.wulkanowy.data.db.dao.GradeDao -import io.github.wulkanowy.data.db.dao.GradeSummaryDao -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeLocal @Inject constructor( - private val gradeDb: GradeDao, - private val gradeSummaryDb: GradeSummaryDao -) { - - suspend fun saveGrades(grades: List) { - gradeDb.insertAll(grades) - } - - suspend fun deleteGrades(grades: List) { - gradeDb.deleteAll(grades) - } - - suspend fun updateGrades(grades: List) { - gradeDb.updateAll(grades) - } - - suspend fun updateGradesSummary(gradesSummary: List) { - gradeSummaryDb.updateAll(gradesSummary) - } - - fun getGradesDetails(semester: Semester): Flow> { - return gradeDb.loadAll(semester.semesterId, semester.studentId) - } - - suspend fun saveGradesSummary(gradesSummary: List) { - gradeSummaryDb.insertAll(gradesSummary) - } - - suspend fun deleteGradesSummary(gradesSummary: List) { - gradeSummaryDb.deleteAll(gradesSummary) - } - - fun getGradesSummary(semester: Semester): Flow> { - return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt deleted file mode 100644 index 9534a8910..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.wulkanowy.data.repositories.grade - -import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getGrades(student: Student, semester: Semester): Pair, List> { - val (details, summary) = sdk - .init(student) - .switchDiary(semester.diaryId, semester.schoolYear) - .getGrades(semester.semesterId) - - return details.map { - Grade( - studentId = semester.studentId, - semesterId = semester.semesterId, - subject = it.subject, - entry = it.entry, - value = it.value, - modifier = it.modifier, - comment = it.comment, - color = it.color, - gradeSymbol = it.symbol, - description = it.description, - weight = it.weight, - weightValue = it.weightValue, - date = it.date, - teacher = it.teacher - ) - } to summary.map { - GradeSummary( - semesterId = semester.semesterId, - studentId = semester.studentId, - position = 0, - subject = it.name, - predictedGrade = it.predicted, - finalGrade = it.final, - pointsSum = it.pointsSum, - proposedPoints = it.proposedPoints, - finalPoints = it.finalPoints, - average = it.average - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt deleted file mode 100644 index e0e2cd4db..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao -import io.github.wulkanowy.data.db.dao.GradeStatisticsDao -import io.github.wulkanowy.data.db.entities.GradePointsStatistics -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeStatisticsLocal @Inject constructor( - private val gradeStatisticsDb: GradeStatisticsDao, - private val gradePointsStatisticsDb: GradePointsStatisticsDao -) { - - fun getGradesStatistics(semester: Semester, isSemester: Boolean): Flow> { - return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester) - } - - fun getGradesPointsStatistics(semester: Semester): Flow> { - return gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) - } - - suspend fun saveGradesStatistics(gradesStatistics: List) { - gradeStatisticsDb.insertAll(gradesStatistics) - } - - suspend fun saveGradesPointsStatistics(gradePointsStatistics: List) { - gradePointsStatisticsDb.insertAll(gradePointsStatistics) - } - - suspend fun deleteGradesStatistics(gradesStatistics: List) { - gradeStatisticsDb.deleteAll(gradesStatistics) - } - - suspend fun deleteGradesPointsStatistics(gradesPointsStatistics: List) { - gradePointsStatisticsDb.deleteAll(gradesPointsStatistics) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt deleted file mode 100644 index 1ff8132fc..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import io.github.wulkanowy.data.db.entities.GradePointsStatistics -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeStatisticsRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getGradeStatistics(student: Student, semester: Semester, isSemester: Boolean): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).let { - if (isSemester) it.getGradesAnnualStatistics(semester.semesterId) - else it.getGradesPartialStatistics(semester.semesterId) - }.map { - GradeStatistics( - semesterId = semester.semesterId, - studentId = semester.studentId, - subject = it.subject, - grade = it.gradeValue, - amount = it.amount, - semester = isSemester - ) - } - } - - suspend fun getGradePointsStatistics(student: Student, semester: Semester): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getGradesPointsStatistics(semester.semesterId) - .map { - GradePointsStatistics( - semesterId = semester.semesterId, - studentId = semester.studentId, - subject = it.subject, - others = it.others, - student = it.student - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt deleted file mode 100644 index 52ca705f7..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt +++ /dev/null @@ -1,72 +0,0 @@ -package io.github.wulkanowy.data.repositories.gradestatistics - -import io.github.wulkanowy.data.db.entities.GradePointsStatistics -import io.github.wulkanowy.data.db.entities.GradeStatistics -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.GradeStatisticsItem -import io.github.wulkanowy.ui.modules.grade.statistics.ViewType -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.uniqueSubtract -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class GradeStatisticsRepository @Inject constructor( - private val local: GradeStatisticsLocal, - private val remote: GradeStatisticsRemote -) { - - fun getGradesStatistics(student: Student, semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getGradesStatistics(semester, isSemester) }, - fetch = { remote.getGradeStatistics(student, semester, isSemester) }, - saveFetchResult = { old, new -> - local.deleteGradesStatistics(old uniqueSubtract new) - local.saveGradesStatistics(new uniqueSubtract old) - }, - mapResult = { items -> - when (subjectName) { - "Wszystkie" -> items.groupBy { it.grade }.map { - GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key, - it.value.fold(0) { acc, e -> acc + e.amount }, false) - } + items - else -> items.filter { it.subject == subjectName } - }.mapToStatisticItems() - } - ) - - fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getGradesPointsStatistics(semester) }, - fetch = { remote.getGradePointsStatistics(student, semester) }, - saveFetchResult = { old, new -> - local.deleteGradesPointsStatistics(old uniqueSubtract new) - local.saveGradesPointsStatistics(new uniqueSubtract old) - }, - mapResult = { items -> - when (subjectName) { - "Wszystkie" -> items - else -> items.filter { it.subject == subjectName } - }.mapToStatisticsItem() - } - ) - - private fun List.mapToStatisticItems() = groupBy { it.subject }.map { - GradeStatisticsItem( - type = ViewType.PARTIAL, - partial = it.value - .sortedByDescending { item -> item.grade } - .filter { item -> item.amount != 0 }, - points = null - ) - } - - private fun List.mapToStatisticsItem() = map { - GradeStatisticsItem( - type = ViewType.POINTS, - partial = emptyList(), - points = it - ) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt deleted file mode 100644 index f2cbb8031..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkLocal.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.homework - -import io.github.wulkanowy.data.db.dao.HomeworkDao -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.Flow -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) { - - suspend fun saveHomework(homework: List) { - homeworkDb.insertAll(homework) - } - - suspend fun deleteHomework(homework: List) { - homeworkDb.deleteAll(homework) - } - - suspend fun updateHomework(homework: List) { - homeworkDb.updateAll(homework) - } - - fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow> { - return homeworkDb.loadAll(semester.semesterId, semester.studentId, startDate, endDate) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt deleted file mode 100644 index 32109877a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt +++ /dev/null @@ -1,32 +0,0 @@ -package io.github.wulkanowy.data.repositories.homework - -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class HomeworkRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getHomework(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getHomework(startDate, endDate) - .map { - Homework( - semesterId = semester.semesterId, - studentId = semester.studentId, - date = it.date, - entryDate = it.entryDate, - subject = it.subject, - content = it.content, - teacher = it.teacher, - teacherSymbol = it.teacherSymbol, - attachments = it.attachments.map { attachment -> attachment.url to attachment.name } - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt deleted file mode 100644 index 54397ea02..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRepository.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.data.repositories.homework - -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.monday -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.sunday -import io.github.wulkanowy.utils.uniqueSubtract -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class HomeworkRepository @Inject constructor( - private val local: HomeworkLocal, - private val remote: HomeworkRemote -) { - - fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getHomework(semester, start.monday, end.sunday) }, - fetch = { remote.getHomework(student, semester, start.monday, end.sunday) }, - saveFetchResult = { old, new -> - local.deleteHomework(old uniqueSubtract new) - local.saveHomework(new uniqueSubtract old) - } - ) - - suspend fun toggleDone(homework: Homework) { - local.updateHomework(listOf(homework.apply { - isDone = !isDone - })) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt deleted file mode 100644 index 0c3156d1e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberLocal.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import io.github.wulkanowy.data.db.dao.LuckyNumberDao -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Student -import kotlinx.coroutines.flow.Flow -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumberDao) { - - suspend fun saveLuckyNumber(luckyNumber: LuckyNumber?) { - luckyNumberDb.insertAll(listOfNotNull(luckyNumber)) - } - - suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) { - luckyNumberDb.updateAll(listOfNotNull(luckyNumber)) - } - - suspend fun deleteLuckyNumber(luckyNumber: LuckyNumber?) { - luckyNumberDb.deleteAll(listOfNotNull(luckyNumber)) - } - - fun getLuckyNumber(student: Student, date: LocalDate): Flow { - return luckyNumberDb.load(student.studentId, date) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt deleted file mode 100644 index 2872957d0..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class LuckyNumberRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getLuckyNumber(student: Student): LuckyNumber? { - return sdk.init(student).getLuckyNumber(student.schoolShortName)?.let { - LuckyNumber( - studentId = student.studentId, - date = LocalDate.now(), - luckyNumber = it - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt deleted file mode 100644 index 173ce7e45..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.repositories.luckynumber - -import io.github.wulkanowy.data.db.entities.LuckyNumber -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.networkBoundResource -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map -import java.time.LocalDate.now -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class LuckyNumberRepository @Inject constructor( - private val local: LuckyNumberLocal, - private val remote: LuckyNumberRemote -) { - - fun getLuckyNumber(student: Student, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource( - shouldFetch = { it == null || forceRefresh }, - query = { local.getLuckyNumber(student, now()) }, - fetch = { remote.getLuckyNumber(student) }, - saveFetchResult = { old, new -> - if (new != old) { - old?.let { local.deleteLuckyNumber(it) } - local.saveLuckyNumber(new?.apply { - if (notify) isNotified = false - }) - } - } - ) - - suspend fun getNotNotifiedLuckyNumber(student: Student) = - local.getLuckyNumber(student, now()).map { if (it?.isNotified == false) it else null }.first() - - suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) = local.updateLuckyNumber(luckyNumber) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt deleted file mode 100644 index f1c8eaf08..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageLocal.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.github.wulkanowy.data.repositories.message - -import io.github.wulkanowy.data.db.dao.MessageAttachmentDao -import io.github.wulkanowy.data.db.dao.MessagesDao -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.MessageAttachment -import io.github.wulkanowy.data.db.entities.MessageWithAttachment -import io.github.wulkanowy.data.db.entities.Student -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class MessageLocal @Inject constructor( - private val messagesDb: MessagesDao, - private val messageAttachmentDao: MessageAttachmentDao -) { - - suspend fun saveMessages(messages: List) { - messagesDb.insertAll(messages) - } - - suspend fun updateMessages(messages: List) { - messagesDb.updateAll(messages) - } - - suspend fun deleteMessages(messages: List) { - messagesDb.deleteAll(messages) - } - - fun getMessageWithAttachment(student: Student, message: Message): Flow { - return messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) - } - - suspend fun saveMessageAttachments(attachments: List) { - messageAttachmentDao.insertAttachments(attachments) - } - - fun getMessages(student: Student, folder: MessageFolder): Flow> { - return messagesDb.loadAll(student.id.toInt(), folder.id) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt deleted file mode 100644 index 044a13a28..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt +++ /dev/null @@ -1,78 +0,0 @@ -package io.github.wulkanowy.data.repositories.message - -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.MessageAttachment -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.sdk.pojo.Folder -import io.github.wulkanowy.sdk.pojo.SentMessage -import io.github.wulkanowy.utils.init -import java.time.LocalDateTime.now -import javax.inject.Inject -import javax.inject.Singleton -import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient - -@Singleton -class MessageRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getMessages(student: Student, semester: Semester, folder: MessageFolder): List { - return sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now()).map { - Message( - studentId = student.id.toInt(), - realId = it.id ?: 0, - messageId = it.messageId ?: 0, - sender = it.sender?.name.orEmpty(), - senderId = it.sender?.loginId ?: 0, - recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów", - subject = it.subject.trim(), - date = it.date ?: now(), - folderId = it.folderId, - unread = it.unread ?: false, - removed = it.removed, - hasAttachments = it.hasAttachments - ).apply { - content = it.content.orEmpty() - unreadBy = it.unreadBy ?: 0 - readBy = it.readBy ?: 0 - } - } - } - - suspend fun getMessagesContentDetails(student: Student, message: Message, markAsRead: Boolean = false): Pair> { - return sdk.init(student).getMessageDetails(message.messageId, message.folderId, markAsRead, message.realId).let { details -> - details.content to details.attachments.map { - MessageAttachment( - realId = it.id, - messageId = it.messageId, - oneDriveId = it.oneDriveId, - url = it.url, - filename = it.filename - ) - } - } - } - - suspend fun sendMessage(student: Student, subject: String, content: String, recipients: List): SentMessage { - return sdk.init(student).sendMessage( - subject = subject, - content = content, - recipients = recipients.map { - SdkRecipient( - id = it.realId, - name = it.realName, - loginId = it.loginId, - reportingUnitId = it.unitId, - role = it.role, - hash = it.hash, - shortName = it.name - ) - } - ) - } - - suspend fun deleteMessage(student: Student, message: Message): Boolean { - return sdk.init(student).deleteMessages(listOf(message.messageId), message.folderId) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt deleted file mode 100644 index bb9326992..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt +++ /dev/null @@ -1,76 +0,0 @@ -package io.github.wulkanowy.data.repositories.message - -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED -import io.github.wulkanowy.sdk.pojo.SentMessage -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.uniqueSubtract -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import timber.log.Timber -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class MessageRepository @Inject constructor( - private val local: MessageLocal, - private val remote: MessageRemote -) { - - fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getMessages(student, folder) }, - fetch = { remote.getMessages(student, semester, folder) }, - saveFetchResult = { old, new -> - local.deleteMessages(old uniqueSubtract new) - local.saveMessages((new uniqueSubtract old).onEach { - it.isNotified = !notify - }) - } - ) - - fun getMessage(student: Student, message: Message, markAsRead: Boolean = false) = networkBoundResource( - shouldFetch = { - checkNotNull(it, { "This message no longer exist!" }) - Timber.d("Message content in db empty: ${it.message.content.isEmpty()}") - it.message.unread || it.message.content.isEmpty() - }, - query = { local.getMessageWithAttachment(student, message) }, - fetch = { remote.getMessagesContentDetails(student, it!!.message, markAsRead) }, - saveFetchResult = { old, (downloadedMessage, attachments) -> - checkNotNull(old, { "Fetched message no longer exist!" }) - local.updateMessages(listOf(old.message.copy(unread = !markAsRead).apply { - id = old.message.id - content = content.ifBlank { downloadedMessage } - })) - local.saveMessageAttachments(attachments) - Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read") - } - ) - - fun getNotNotifiedMessages(student: Student): Flow> { - return local.getMessages(student, RECEIVED).map { it.filter { message -> !message.isNotified && message.unread } } - } - - suspend fun updateMessages(messages: List) { - return local.updateMessages(messages) - } - - suspend fun sendMessage(student: Student, subject: String, content: String, recipients: List): SentMessage { - return remote.sendMessage(student, subject, content, recipients) - } - - suspend fun deleteMessage(student: Student, message: Message) { - val isDeleted = remote.deleteMessage(student, message) - - if (message.folderId != MessageFolder.TRASHED.id) { - if (isDeleted) local.updateMessages(listOf(message.copy(folderId = MessageFolder.TRASHED.id).apply { - id = message.id - content = message.content - })) - } else local.deleteMessages(listOf(message)) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceLocal.kt deleted file mode 100644 index 0ccb3d7ef..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.mobiledevice - -import io.github.wulkanowy.data.db.dao.MobileDeviceDao -import io.github.wulkanowy.data.db.entities.MobileDevice -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class MobileDeviceLocal @Inject constructor(private val mobileDb: MobileDeviceDao) { - - suspend fun saveDevices(devices: List) { - mobileDb.insertAll(devices) - } - - suspend fun deleteDevices(devices: List) { - mobileDb.deleteAll(devices) - } - - fun getDevices(semester: Semester): Flow> { - return mobileDb.loadAll(semester.studentId) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRemote.kt deleted file mode 100644 index 907e965ce..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRemote.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.wulkanowy.data.repositories.mobiledevice - -import io.github.wulkanowy.data.db.entities.MobileDevice -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.MobileDeviceToken -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class MobileDeviceRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getDevices(student: Student, semester: Semester): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getRegisteredDevices() - .map { - MobileDevice( - studentId = semester.studentId, - date = it.createDate, - deviceId = it.id, - name = it.name - ) - } - } - - suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice): Boolean { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .unregisterDevice(device.deviceId) - } - - suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getToken() - .let { - MobileDeviceToken( - token = it.token, - symbol = it.symbol, - pin = it.pin, - qr = it.qrCodeImage - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRepository.kt deleted file mode 100644 index 65526ef86..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRepository.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.repositories.mobiledevice - -import io.github.wulkanowy.data.db.entities.MobileDevice -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.pojos.MobileDeviceToken -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.uniqueSubtract -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class MobileDeviceRepository @Inject constructor( - private val local: MobileDeviceLocal, - private val remote: MobileDeviceRemote -) { - - fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getDevices(semester) }, - fetch = { remote.getDevices(student, semester) }, - saveFetchResult = { old, new -> - local.deleteDevices(old uniqueSubtract new) - local.saveDevices(new uniqueSubtract old) - } - ) - - suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) { - remote.unregisterDevice(student, semester, device) - local.deleteDevices(listOf(device)) - } - - suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken { - return remote.getToken(student, semester) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt deleted file mode 100644 index 85ba5e223..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteLocal.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.github.wulkanowy.data.repositories.note - -import io.github.wulkanowy.data.db.dao.NoteDao -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Student -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NoteLocal @Inject constructor(private val noteDb: NoteDao) { - - suspend fun saveNotes(notes: List) { - noteDb.insertAll(notes) - } - - suspend fun updateNotes(notes: List) { - noteDb.updateAll(notes) - } - - suspend fun deleteNotes(notes: List) { - noteDb.deleteAll(notes) - } - - fun getNotes(student: Student): Flow> { - return noteDb.loadAll(student.studentId) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt deleted file mode 100644 index 0e488b7d9..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.wulkanowy.data.repositories.note - -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NoteRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getNotes(student: Student, semester: Semester): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getNotes(semester.semesterId) - .map { - Note( - studentId = semester.studentId, - date = it.date, - teacher = it.teacher, - teacherSymbol = it.teacherSymbol, - category = it.category, - categoryType = it.categoryType.id, - isPointsShow = it.showPoints, - points = it.points, - content = it.content - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt deleted file mode 100644 index 6cf62ba22..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRepository.kt +++ /dev/null @@ -1,45 +0,0 @@ -package io.github.wulkanowy.data.repositories.note - -import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.uniqueSubtract -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class NoteRepository @Inject constructor( - private val local: NoteLocal, - private val remote: NoteRemote -) { - - fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getNotes(student) }, - fetch = { remote.getNotes(student, semester) }, - saveFetchResult = { old, new -> - local.deleteNotes(old uniqueSubtract new) - local.saveNotes((new uniqueSubtract old).onEach { - if (it.date >= student.registrationDate.toLocalDate()) it.apply { - isRead = false - if (notify) isNotified = false - } - }) - } - ) - - fun getNotNotifiedNotes(student: Student): Flow> { - return local.getNotes(student).map { it.filter { note -> !note.isNotified } } - } - - suspend fun updateNote(note: Note) { - local.updateNotes(listOf(note)) - } - - suspend fun updateNotes(notes: List) { - return local.updateNotes(notes) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt deleted file mode 100644 index fac1645e4..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import io.github.wulkanowy.data.db.dao.RecipientDao -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class RecipientLocal @Inject constructor(private val recipientDb: RecipientDao) { - - suspend fun getRecipients(student: Student, role: Int, unit: ReportingUnit): List { - return recipientDb.load(student.studentId, role, unit.realId) - } - - suspend fun saveRecipients(recipients: List): List { - return recipientDb.insertAll(recipients) - } - - suspend fun deleteRecipients(recipients: List) { - recipientDb.deleteAll(recipients) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt deleted file mode 100644 index a5318e77f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import javax.inject.Inject -import javax.inject.Singleton -import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient - -@Singleton -class RecipientRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getRecipients(student: Student, role: Int, unit: ReportingUnit): List { - return sdk.init(student).getRecipients(unit.realId, role) - .map { it.toRecipient() } - } - - suspend fun getMessageRecipients(student: Student, message: Message): List { - return sdk.init(student).getMessageRecipients(message.messageId, message.senderId) - .map { it.toRecipient() } - } - - private fun SdkRecipient.toRecipient(): Recipient { - return Recipient( - studentId = sdk.studentId, - realId = id, - realName = name, - name = shortName, - hash = hash, - loginId = loginId, - role = role, - unitId = reportingUnitId ?: 0 - ) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt deleted file mode 100644 index f5e876b03..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt +++ /dev/null @@ -1,36 +0,0 @@ -package io.github.wulkanowy.data.repositories.recipient - -import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.db.entities.Recipient -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.uniqueSubtract -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class RecipientRepository @Inject constructor( - private val local: RecipientLocal, - private val remote: RecipientRemote -) { - - suspend fun refreshRecipients(student: Student, role: Int, unit: ReportingUnit) { - val new = remote.getRecipients(student, role, unit) - val old = local.getRecipients(student, role, unit) - - local.deleteRecipients(old uniqueSubtract new) - local.saveRecipients(new uniqueSubtract old) - } - - suspend fun getRecipients(student: Student, role: Int, unit: ReportingUnit): List { - return local.getRecipients(student, role, unit).ifEmpty { - refreshRecipients(student, role, unit) - - local.getRecipients(student, role, unit) - } - } - - suspend fun getMessageRecipients(student: Student, message: Message): List { - return remote.getMessageRecipients(student, message) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recover/RecoverRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recover/RecoverRepository.kt deleted file mode 100644 index 3117a606a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recover/RecoverRepository.kt +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.wulkanowy.data.repositories.recover - -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class RecoverRepository @Inject constructor(private val remote: RecoverRemote) { - - suspend fun getReCaptchaSiteKey(host: String, symbol: String): Pair { - return remote.getReCaptchaSiteKey(host, symbol) - } - - suspend fun sendRecoverRequest(url: String, symbol: String, email: String, reCaptchaResponse: String): String { - return remote.sendRecoverRequest(url, symbol, email, reCaptchaResponse) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt deleted file mode 100644 index 737f1a04f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitLocal.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.repositories.reportingunit - -import io.github.wulkanowy.data.db.dao.ReportingUnitDao -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ReportingUnitLocal @Inject constructor(private val reportingUnitDb: ReportingUnitDao) { - - suspend fun getReportingUnits(student: Student): List { - return reportingUnitDb.load(student.studentId) - } - - suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? { - return reportingUnitDb.loadOne(student.studentId, unitId) - } - - suspend fun saveReportingUnits(reportingUnits: List): List { - return reportingUnitDb.insertAll(reportingUnits) - } - - suspend fun deleteReportingUnits(reportingUnits: List) { - reportingUnitDb.deleteAll(reportingUnits) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt deleted file mode 100644 index 6b11c2cc9..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.reportingunit - -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ReportingUnitRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getReportingUnits(student: Student): List { - return sdk.init(student).getReportingUnits().map { unit -> - ReportingUnit( - studentId = sdk.studentId, - realId = unit.id, - roles = unit.roles, - senderId = unit.senderId, - senderName = unit.senderName, - shortName = unit.short - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt deleted file mode 100644 index ff5839460..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.github.wulkanowy.data.repositories.reportingunit - -import io.github.wulkanowy.data.db.entities.ReportingUnit -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.uniqueSubtract -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ReportingUnitRepository @Inject constructor( - private val local: ReportingUnitLocal, - private val remote: ReportingUnitRemote -) { - - suspend fun refreshReportingUnits(student: Student) { - val new = remote.getReportingUnits(student) - val old = local.getReportingUnits(student) - - local.deleteReportingUnits(old.uniqueSubtract(new)) - local.saveReportingUnits(new.uniqueSubtract(old)) - } - - suspend fun getReportingUnits(student: Student): List { - return local.getReportingUnits(student).ifEmpty { - refreshReportingUnits(student) - - local.getReportingUnits(student) - } - } - - suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? { - return local.getReportingUnit(student, unitId) ?: run { - refreshReportingUnits(student) - - return local.getReportingUnit(student, unitId) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolLocal.kt deleted file mode 100644 index bc1b2f446..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolLocal.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.data.repositories.school - -import io.github.wulkanowy.data.db.dao.SchoolDao -import io.github.wulkanowy.data.db.entities.School -import io.github.wulkanowy.data.db.entities.Semester -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject - -class SchoolLocal @Inject constructor(private val schoolDb: SchoolDao) { - - suspend fun saveSchool(school: School) { - schoolDb.insertAll(listOf(school)) - } - - suspend fun deleteSchool(school: School) { - schoolDb.deleteAll(listOf(school)) - } - - fun getSchool(semester: Semester): Flow { - return schoolDb.load(semester.studentId, semester.classId) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolRemote.kt deleted file mode 100644 index 4d2e0cd6c..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolRemote.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.repositories.school - -import io.github.wulkanowy.data.db.entities.School -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import javax.inject.Inject - -class SchoolRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getSchoolInfo(student: Student, semester: Semester): School { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getSchool() - .let { - School( - studentId = semester.studentId, - classId = semester.classId, - name = it.name, - address = it.address, - contact = it.contact, - headmaster = it.headmaster, - pedagogue = it.pedagogue - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolRepository.kt deleted file mode 100644 index 4c84c3194..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolRepository.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.repositories.school - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.networkBoundResource -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SchoolRepository @Inject constructor( - private val local: SchoolLocal, - private val remote: SchoolRemote -) { - - fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource( - shouldFetch = { it == null || forceRefresh }, - query = { local.getSchool(semester) }, - fetch = { remote.getSchoolInfo(student, semester) }, - saveFetchResult = { old, new -> - if (new != old && old != null) { - local.deleteSchool(old) - local.saveSchool(new) - } - local.saveSchool(new) - } - ) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt deleted file mode 100644 index 8ecf1595a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterLocal.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.repositories.semester - -import io.github.wulkanowy.data.db.dao.SemesterDao -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SemesterLocal @Inject constructor(private val semesterDb: SemesterDao) { - - suspend fun saveSemesters(semesters: List) { - semesterDb.insertSemesters(semesters) - } - - suspend fun deleteSemesters(semesters: List) { - semesterDb.deleteAll(semesters) - } - - suspend fun getSemesters(student: Student): List { - return semesterDb.loadAll(student.studentId, student.classId) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt deleted file mode 100644 index e1a920b6f..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.data.repositories.semester - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SemesterRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getSemesters(student: Student): List { - return sdk.init(student).getSemesters().map { - Semester( - studentId = student.studentId, - diaryId = it.diaryId, - diaryName = it.diaryName, - schoolYear = it.schoolYear, - semesterId = it.semesterId, - semesterName = it.semesterNumber, - start = it.start, - end = it.end, - classId = it.classId, - unitId = it.unitId - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt deleted file mode 100644 index c01d1d049..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt +++ /dev/null @@ -1,59 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext -import io.github.wulkanowy.data.db.dao.StudentDao -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.DispatchersProvider -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 StudentLocal @Inject constructor( - private val studentDb: StudentDao, - private val dispatchers: DispatchersProvider, - @ApplicationContext private val context: Context -) { - - suspend fun saveStudents(students: List) = withContext(dispatchers.backgroundThread) { - studentDb.insertAll(students.map { - if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) it.copy(password = encrypt(it.password, context)) - else it - }) - } - - suspend fun getStudents(decryptPass: Boolean) = withContext(dispatchers.backgroundThread) { - studentDb.loadStudentsWithSemesters().map { - it.apply { - if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) student.password = decrypt(student.password) - } - } - } - - suspend fun getStudentById(id: Int) = withContext(dispatchers.backgroundThread) { - studentDb.loadById(id)?.apply { - if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password) - } - } - - suspend fun getCurrentStudent(decryptPass: Boolean) = withContext(dispatchers.backgroundThread) { - studentDb.loadCurrent()?.apply { - if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password) - } - } - - suspend fun setCurrentStudent(student: Student) = withContext(dispatchers.backgroundThread) { - studentDb.run { - resetCurrent() - updateCurrent(student.id) - } - } - - suspend fun logoutStudent(student: Student) = withContext(dispatchers.backgroundThread) { - studentDb.delete(student) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt deleted file mode 100644 index dcb126233..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import io.github.wulkanowy.data.mappers.mapToEntities -import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.sdk.Sdk -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class StudentRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getStudentsMobileApi(token: String, pin: String, symbol: String): List { - return sdk.getStudentsFromMobileApi(token, pin, symbol, "").mapToEntities() - } - - suspend fun getStudentsScrapper(email: String, password: String, scrapperBaseUrl: String, symbol: String): List { - return sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol).mapToEntities(password) - } - - suspend fun getStudentsHybrid(email: String, password: String, scrapperBaseUrl: String, symbol: String): List { - return sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol).mapToEntities(password) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt deleted file mode 100644 index 19e0e2086..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt +++ /dev/null @@ -1,57 +0,0 @@ -package io.github.wulkanowy.data.repositories.student - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.exceptions.NoCurrentStudentException -import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.data.repositories.semester.SemesterLocal -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class StudentRepository @Inject constructor( - private val local: StudentLocal, - private val semestersLocal: SemesterLocal, - private val remote: StudentRemote -) { - - suspend fun isStudentSaved(): Boolean = local.getStudents(false).isNotEmpty() - - suspend fun isCurrentStudentSet(): Boolean = local.getCurrentStudent(false)?.isCurrent ?: false - - suspend fun getStudentsApi(pin: String, symbol: String, token: String): List { - return remote.getStudentsMobileApi(token, pin, symbol) - } - - suspend fun getStudentsScrapper(email: String, password: String, endpoint: String, symbol: String): List { - return remote.getStudentsScrapper(email, password, endpoint, symbol) - } - - suspend fun getStudentsHybrid(email: String, password: String, endpoint: String, symbol: String): List { - return remote.getStudentsHybrid(email, password, endpoint, symbol) - } - - suspend fun getSavedStudents(decryptPass: Boolean = true): List { - return local.getStudents(decryptPass) - } - - suspend fun getStudentById(id: Int): Student { - return local.getStudentById(id) ?: throw NoCurrentStudentException() - } - - suspend fun getCurrentStudent(decryptPass: Boolean = true): Student { - return local.getCurrentStudent(decryptPass) ?: throw NoCurrentStudentException() - } - - suspend fun saveStudents(studentsWithSemesters: List): List { - semestersLocal.saveSemesters(studentsWithSemesters.flatMap { it.semesters }) - return local.saveStudents(studentsWithSemesters.map { it.student }) - } - - suspend fun switchStudent(studentWithSemesters: StudentWithSemesters) { - return local.setCurrentStudent(studentWithSemesters.student) - } - - suspend fun logoutStudent(student: Student) { - return local.logoutStudent(student) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt deleted file mode 100644 index e225a381e..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectLocal.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.wulkanowy.data.repositories.subject - -import io.github.wulkanowy.data.db.dao.SubjectDao -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Subject -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SubjectLocal @Inject constructor(private val subjectDao: SubjectDao) { - - fun getSubjects(semester: Semester): Flow> { - return subjectDao.loadAll(semester.diaryId, semester.studentId) - } - - suspend fun saveSubjects(subjects: List) { - subjectDao.insertAll(subjects) - } - - suspend fun deleteSubjects(subjects: List) { - subjectDao.deleteAll(subjects) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt deleted file mode 100644 index 624a5a00a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt +++ /dev/null @@ -1,26 +0,0 @@ -package io.github.wulkanowy.data.repositories.subject - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.Subject -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SubjectRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getSubjects(student: Student, semester: Semester): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getSubjects() - .map { - Subject( - studentId = semester.studentId, - diaryId = semester.diaryId, - name = it.name, - realId = it.id - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt deleted file mode 100644 index 60a0c3e71..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRepository.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.subject - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.uniqueSubtract -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class SubjectRepository @Inject constructor( - private val local: SubjectLocal, - private val remote: SubjectRemote -) { - - fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getSubjects(semester) }, - fetch = { remote.getSubjects(student, semester) }, - saveFetchResult = { old, new -> - local.deleteSubjects(old uniqueSubtract new) - local.saveSubjects(new uniqueSubtract old) - } - ) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherLocal.kt deleted file mode 100644 index 908f45a1a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherLocal.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.wulkanowy.data.repositories.teacher - -import io.github.wulkanowy.data.db.dao.TeacherDao -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Teacher -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject - -class TeacherLocal @Inject constructor(private val teacherDb: TeacherDao) { - - suspend fun saveTeachers(teachers: List) { - teacherDb.insertAll(teachers) - } - - suspend fun deleteTeachers(teachers: List) { - teacherDb.deleteAll(teachers) - } - - fun getTeachers(semester: Semester): Flow> { - return teacherDb.loadAll(semester.studentId, semester.classId) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherRemote.kt deleted file mode 100644 index 1d1caa686..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherRemote.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.github.wulkanowy.data.repositories.teacher - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.Teacher -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.init -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TeacherRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getTeachers(student: Student, semester: Semester): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getTeachers(semester.semesterId) - .map { - Teacher( - studentId = semester.studentId, - name = it.name, - subject = it.subject, - shortName = it.short, - classId = semester.classId - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherRepository.kt deleted file mode 100644 index df25a53ec..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherRepository.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.teacher - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.uniqueSubtract -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TeacherRepository @Inject constructor( - private val local: TeacherLocal, - private val remote: TeacherRemote -) { - - fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getTeachers(semester) }, - fetch = { remote.getTeachers(student, semester) }, - saveFetchResult = { old, new -> - local.deleteTeachers(old uniqueSubtract new) - local.saveTeachers(new uniqueSubtract old) - } - ) -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt deleted file mode 100644 index df4bfb20a..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocal.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import io.github.wulkanowy.data.db.dao.TimetableDao -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Timetable -import kotlinx.coroutines.flow.Flow -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao) { - - suspend fun saveTimetable(timetables: List) { - timetableDb.insertAll(timetables) - } - - suspend fun deleteTimetable(timetables: List) { - timetableDb.deleteAll(timetables) - } - - fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow> { - return timetableDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate) - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt deleted file mode 100644 index eef8729e8..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -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.sdk.Sdk -import io.github.wulkanowy.utils.init -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TimetableRemote @Inject constructor(private val sdk: Sdk) { - - suspend fun getTimetable(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): List { - return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) - .getTimetable(startDate, endDate) - .map { - Timetable( - studentId = semester.studentId, - diaryId = semester.diaryId, - number = it.number, - start = it.start, - end = it.end, - date = it.date, - subject = it.subject, - subjectOld = it.subjectOld, - group = it.group, - room = it.room, - roomOld = it.roomOld, - teacher = it.teacher, - teacherOld = it.teacherOld, - info = it.info, - isStudentPlan = it.studentPlan, - changes = it.changes, - canceled = it.canceled - ) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt deleted file mode 100644 index ee2734aaf..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.github.wulkanowy.data.repositories.timetable - -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper -import io.github.wulkanowy.utils.monday -import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.sunday -import io.github.wulkanowy.utils.uniqueSubtract -import kotlinx.coroutines.flow.map -import java.time.LocalDate -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class TimetableRepository @Inject constructor( - private val local: TimetableLocal, - private val remote: TimetableRemote, - private val schedulerHelper: TimetableNotificationSchedulerHelper -) { - - fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( - shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getTimetable(semester, start.monday, end.sunday).map { schedulerHelper.scheduleNotifications(it, student); it } }, - fetch = { remote.getTimetable(student, semester, start.monday, end.sunday) }, - saveFetchResult = { old, new -> - local.deleteTimetable(old.uniqueSubtract(new).also { schedulerHelper.cancelScheduled(it) }) - local.saveTimetable(new.uniqueSubtract(old).also { schedulerHelper.scheduleNotifications(it, student) }.map { item -> - item.also { new -> - old.singleOrNull { new.start == it.start }?.let { old -> - return@map new.copy( - room = if (new.room.isEmpty()) old.room else new.room, - teacher = if (new.teacher.isEmpty() && !new.changes && !old.changes) old.teacher else new.teacher - ) - } - } - }) - }, - filterResult = { it.filter { item -> item.date in start..end } } - ) -} diff --git a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt index 9133c34ed..4efb4d84a 100644 --- a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt +++ b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt @@ -6,13 +6,13 @@ import com.yariksoffice.lingver.Lingver import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.components.ApplicationComponent import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent import io.github.wulkanowy.utils.DispatchersProvider import javax.inject.Singleton @Module -@InstallIn(ApplicationComponent::class) +@InstallIn(SingletonComponent::class) internal class AppModule { @Singleton diff --git a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt index ac5a84e8f..891f07da0 100644 --- a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt +++ b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt @@ -9,8 +9,8 @@ import dagger.Binds import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.components.ApplicationComponent import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent import dagger.multibindings.IntoSet import io.github.wulkanowy.services.sync.channels.Channel import io.github.wulkanowy.services.sync.channels.DebugChannel @@ -38,7 +38,7 @@ import javax.inject.Singleton @Suppress("unused") @Module -@InstallIn(ApplicationComponent::class) +@InstallIn(SingletonComponent::class) abstract class ServicesModule { companion object { diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt index 592d0919e..8eefc032f 100644 --- a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt +++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt @@ -12,7 +12,7 @@ import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.services.HiltBroadcastReceiver import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID import io.github.wulkanowy.ui.modules.main.MainActivity diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt index 5c4b916bc..c0eba2f92 100644 --- a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt +++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt @@ -11,7 +11,7 @@ import androidx.core.app.NotificationManagerCompat import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_END import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_NEXT_ROOM import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.LESSON_NEXT_TITLE @@ -72,18 +72,22 @@ class TimetableNotificationSchedulerHelper @Inject constructor( withContext(dispatchersProvider.backgroundThread) { lessons.groupBy { it.date } .map { it.value.sortedBy { lesson -> lesson.start } } - .map { it.filter { lesson -> !lesson.canceled && lesson.isStudentPlan } } + .map { it.filter { lesson -> lesson.isStudentPlan } } .map { day -> - day.forEachIndexed { index, lesson -> - val intent = createIntent(student, lesson, day.getOrNull(index + 1)) + val canceled = day.filter { it.canceled } + val active = day.filter { !it.canceled } + + cancelScheduled(canceled) + active.forEachIndexed { index, lesson -> + val intent = createIntent(student, lesson, active.getOrNull(index + 1)) if (lesson.start > now()) { - scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, day, lesson)) + scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, active, lesson)) } if (lesson.end > now()) { scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start) - if (day.lastIndex == index) { + if (active.lastIndex == index) { scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end) } } 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 47a949273..b94d97e33 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt @@ -18,7 +18,7 @@ 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.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.sync.channels.Channel import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.isHolidays diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt index 13326ca06..a428b702b 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -11,9 +11,9 @@ import androidx.work.CoroutineWorker import androidx.work.Data import androidx.work.WorkerParameters import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +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.sdk.exception.FeatureNotAvailableException import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException import io.github.wulkanowy.services.sync.channels.DebugChannel diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt index 9e4ab9025..cbe1fe6bd 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt @@ -2,7 +2,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.repositories.attendancesummary.AttendanceSummaryRepository +import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository import io.github.wulkanowy.utils.waitForResult import javax.inject.Inject diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt index ecf0be018..788e4ea2c 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt @@ -2,7 +2,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.repositories.attendance.AttendanceRepository +import io.github.wulkanowy.data.repositories.AttendanceRepository import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.sunday import io.github.wulkanowy.utils.waitForResult diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt index 212ea6327..17bd61292 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt @@ -2,7 +2,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.repositories.completedlessons.CompletedLessonsRepository +import io.github.wulkanowy.data.repositories.CompletedLessonsRepository import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.sunday import io.github.wulkanowy.utils.waitForResult diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt index 899d45cba..b75499301 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt @@ -2,7 +2,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.repositories.exam.ExamRepository +import io.github.wulkanowy.data.repositories.ExamRepository import io.github.wulkanowy.utils.waitForResult import java.time.LocalDate.now import javax.inject.Inject diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt index 3d61a423b..4575b419b 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt @@ -2,7 +2,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.repositories.gradestatistics.GradeStatisticsRepository +import io.github.wulkanowy.data.repositories.GradeStatisticsRepository import io.github.wulkanowy.utils.waitForResult import javax.inject.Inject @@ -12,8 +12,8 @@ class GradeStatisticsWork @Inject constructor( override suspend fun doWork(student: Student, semester: Semester) { with(gradeStatisticsRepository) { - getGradesStatistics(student, semester, "Wszystkie", isSemester = true, forceRefresh = true).waitForResult() - getGradesStatistics(student, semester, "Wszystkie", isSemester = false, forceRefresh = true).waitForResult() + getGradesPartialStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult() + getGradesSemesterStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult() getGradesPointsStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult() } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt index 00bce109e..19c26edd0 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt @@ -13,8 +13,8 @@ import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.grade.GradeRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.GradeRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.sync.channels.NewGradesChannel import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt index a07f11073..a16841e9a 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt @@ -2,7 +2,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.repositories.homework.HomeworkRepository +import io.github.wulkanowy.data.repositories.HomeworkRepository import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.sunday import io.github.wulkanowy.utils.waitForResult diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt index 7b9f5ab30..9f5365535 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt @@ -12,8 +12,8 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.LuckyNumberRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView 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 7b32f13bb..93c30b57c 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 @@ -12,9 +12,9 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED -import io.github.wulkanowy.data.repositories.message.MessageRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED +import io.github.wulkanowy.data.repositories.MessageRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.services.sync.channels.NewMessagesChannel import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt index b08896966..50f418ed3 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt @@ -12,8 +12,8 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.note.NoteRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.NoteRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory.NEUTRAL import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory.POSITIVE 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 c433c0ac8..34ab3db04 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 @@ -2,8 +2,8 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.recipient.RecipientRepository -import io.github.wulkanowy.data.repositories.reportingunit.ReportingUnitRepository +import io.github.wulkanowy.data.repositories.RecipientRepository +import io.github.wulkanowy.data.repositories.ReportingUnitRepository import javax.inject.Inject class RecipientWork @Inject constructor( @@ -16,7 +16,7 @@ class RecipientWork @Inject constructor( reportingUnitRepository.getReportingUnits(student).let { units -> units.map { - recipientRepository.refreshRecipients(student, 2, it) + recipientRepository.refreshRecipients(student, it, 2) } } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt index a9abdaa72..7c614c6c5 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt @@ -2,7 +2,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.repositories.teacher.TeacherRepository +import io.github.wulkanowy.data.repositories.TeacherRepository import io.github.wulkanowy.utils.waitForResult import javax.inject.Inject diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt index 3b8c6f5dc..2df2c9dcb 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt @@ -2,7 +2,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.repositories.timetable.TimetableRepository +import io.github.wulkanowy.data.repositories.TimetableRepository import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.sunday import io.github.wulkanowy.utils.waitForResult 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 42c6d3bfd..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 @@ -4,10 +4,10 @@ import android.content.Intent import android.widget.RemoteViewsService import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.data.db.SharedPrefProvider -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.timetable.TimetableRepository +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.modules.timetablewidget.TimetableWidgetFactory import timber.log.Timber import javax.inject.Inject 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 18cd58b41..1c31976ee 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 @@ -3,10 +3,15 @@ package io.github.wulkanowy.ui.base import android.widget.Toast import androidx.fragment.app.DialogFragment import androidx.viewbinding.ViewBinding +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.lifecycleAwareVariable +import javax.inject.Inject abstract class BaseDialogFragment : DialogFragment(), BaseView { + @Inject + lateinit var analyticsHelper: AnalyticsHelper + protected var binding: VB by lifecycleAwareVariable() override fun showError(text: String, error: Throwable) { @@ -28,4 +33,14 @@ abstract class BaseDialogFragment : DialogFragment(), BaseView override fun showErrorDetailsDialog(error: Throwable) { ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } + + override fun onResume() { + super.onResume() + analyticsHelper.setCurrentScreen(requireActivity(), this::class.simpleName) + } + + override fun onPause() { + super.onPause() + analyticsHelper.popCurrentScreen(this::class.simpleName) + } } 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 ba8bfd85f..b222b0abb 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,7 +1,7 @@ package io.github.wulkanowy.ui.base import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.utils.flowWithResource import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers 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 8d6739a15..341ae4590 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 @@ -22,9 +22,11 @@ import io.github.wulkanowy.utils.getString import io.github.wulkanowy.utils.openAppInMarket import io.github.wulkanowy.utils.openEmailClient import io.github.wulkanowy.utils.openInternetBrowser +import okhttp3.internal.http2.StreamResetException import java.io.InterruptedIOException import java.io.PrintWriter import java.io.StringWriter +import java.net.ConnectException import java.net.SocketTimeoutException import java.net.UnknownHostException import javax.inject.Inject @@ -85,6 +87,8 @@ class ErrorDialog : BaseDialogFragment() { errorDialogReport.isEnabled = when (error) { is UnknownHostException, is InterruptedIOException, + is ConnectException, + is StreamResetException, is SocketTimeoutException, is ServiceUnavailableException, is FeatureDisabledException, 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 946e661b1..34dd3ec16 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt @@ -1,7 +1,6 @@ package io.github.wulkanowy.ui.base import android.content.res.Resources -import com.chuckerteam.chucker.api.ChuckerCollector import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException import io.github.wulkanowy.utils.getString @@ -9,7 +8,7 @@ import io.github.wulkanowy.utils.security.ScramblerException import timber.log.Timber import javax.inject.Inject -open class ErrorHandler @Inject constructor(protected val resources: Resources, private val chuckerCollector: ChuckerCollector) { +open class ErrorHandler @Inject constructor(protected val resources: Resources) { var showErrorMessage: (String, Throwable) -> Unit = { _, _ -> } @@ -18,7 +17,6 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources, var onNoCurrentStudent: () -> Unit = {} fun dispatch(error: Throwable) { - chuckerCollector.onError(error.javaClass.simpleName, error) Timber.e(error, "An exception occurred while the Wulkanowy was running") proceed(error) } 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 501348612..a2379c3e7 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 @@ -7,7 +7,7 @@ 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.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository import javax.inject.Inject import javax.inject.Singleton 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 5ec1a66e3..0730033f1 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 @@ -58,6 +58,11 @@ class AboutFragment : BaseFragment(R.layout.fragment_about Triple(getString(R.string.about_discord), getString(R.string.about_discord_summary), getCompatDrawable(R.drawable.ic_about_discord)) } + override val facebookRes: Triple? + get() = context?.run { + Triple(getString(R.string.about_facebook), getString(R.string.about_facebook_summary), getCompatDrawable(R.drawable.ic_about_facebook)) + } + override val homepageRes: Triple? get() = context?.run { Triple(getString(R.string.about_homepage), getString(R.string.about_homepage_summary), getCompatDrawable(R.drawable.ic_about_homepage)) @@ -113,6 +118,10 @@ class AboutFragment : BaseFragment(R.layout.fragment_about context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) } + override fun openFacebookPage() { + context?.openInternetBrowser("https://www.facebook.com/wulkanowy", ::showMessage) + } + override fun openHomepage() { context?.openInternetBrowser("https://wulkanowy.github.io/", ::showMessage) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt index 24e59d36b..fde20267d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt @@ -1,10 +1,10 @@ package io.github.wulkanowy.ui.modules.about -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.AppInfo -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import timber.log.Timber import javax.inject.Inject @@ -12,7 +12,7 @@ class AboutPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val appInfo: AppInfo, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { override fun onAttachView(view: AboutView) { @@ -46,6 +46,11 @@ class AboutPresenter @Inject constructor( openDiscordInvite() analytics.logEvent("about_open", "name" to "discord") } + facebookRes?.first -> { + Timber.i("Opening facebook") + openFacebookPage() + analytics.logEvent("about_open", "name" to "facebook") + } homepageRes?.first -> { Timber.i("Opening homepage") openHomepage() @@ -78,6 +83,7 @@ class AboutPresenter @Inject constructor( feedbackRes, faqRes, discordRes, + facebookRes, homepageRes, licensesRes, privacyRes diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt index 4c4b002f9..54882b302 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt @@ -15,6 +15,8 @@ interface AboutView : BaseView { val discordRes: Triple? + val facebookRes: Triple? + val homepageRes: Triple? val licensesRes: Triple? @@ -31,6 +33,8 @@ interface AboutView : BaseView { fun openDiscordInvite() + fun openFacebookPage() + fun openEmailClient() fun openFaqPage() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt index 78fdb43a1..ef4b540e6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt @@ -2,8 +2,8 @@ package io.github.wulkanowy.ui.modules.about.contributor import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.pojos.Contributor -import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.AppCreatorRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.flowWithResource diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt index 18d257eed..cc430fc2c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt @@ -2,7 +2,7 @@ package io.github.wulkanowy.ui.modules.about.license import com.mikepenz.aboutlibraries.entity.Library import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.DispatchersProvider diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerPresenter.kt index 85eae8e6e..80020c81d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/logviewer/LogViewerPresenter.kt @@ -1,8 +1,8 @@ package io.github.wulkanowy.ui.modules.about.logviewer import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.logger.LoggerRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.LoggerRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.flowWithResource diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt index 4eaee4291..be01f7c54 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt @@ -2,7 +2,7 @@ package io.github.wulkanowy.ui.modules.account import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt index 8e8a6149d..03ec1c842 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt @@ -7,7 +7,7 @@ import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus +import io.github.wulkanowy.data.enums.SentExcuseStatus import io.github.wulkanowy.databinding.ItemAttendanceBinding import io.github.wulkanowy.utils.description import javax.inject.Inject 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 c7caef069..a106bc501 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 @@ -191,8 +191,8 @@ class AttendanceFragment : BaseFragment(R.layout.frag binding. attendanceRecycler.visibility = if (show) VISIBLE else GONE } - override fun hideRefresh() { - binding.attendanceSwipe.isRefreshing = false + override fun showRefresh(show: Boolean) { + binding.attendanceSwipe.isRefreshing = show } override fun showPreButton(show: Boolean) { 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 158f08ba3..68802c0c7 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,13 +3,13 @@ package io.github.wulkanowy.ui.modules.attendance import android.annotation.SuppressLint import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Attendance -import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +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.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResourceIn @@ -34,7 +34,7 @@ class AttendancePresenter @Inject constructor( private val attendanceRepository: AttendanceRepository, private val semesterRepository: SemesterRepository, private val prefRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var baseDate: LocalDate = now().previousOrSameSchoolDay @@ -51,23 +51,23 @@ class AttendancePresenter @Inject constructor( view.initView() Timber.i("Attendance view was initialized") errorHandler.showErrorMessage = ::showErrorViewOnError - loadData(ofEpochDay(date ?: baseDate.toEpochDay())) + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) + loadData() if (currentDate.isHolidays) setBaseDateOnHolidays() - reloadView() } fun onPreviousDay() { view?.finishActionMode() attendanceToExcuseList.clear() - loadData(currentDate.previousSchoolDay) - reloadView() + reloadView(currentDate.previousSchoolDay) + loadData() } fun onNextDay() { view?.finishActionMode() attendanceToExcuseList.clear() - loadData(currentDate.nextSchoolDay) - reloadView() + reloadView(currentDate.nextSchoolDay) + loadData() } fun onPickDate() { @@ -75,13 +75,13 @@ class AttendancePresenter @Inject constructor( } fun onDateSet(year: Int, month: Int, day: Int) { - loadData(LocalDate.of(year, month, day)) - reloadView() + reloadView(LocalDate.of(year, month, day)) + loadData() } fun onSwipeRefresh() { Timber.i("Force refreshing the attendance") - loadData(currentDate, true) + loadData(true) } fun onRetry() { @@ -89,7 +89,7 @@ class AttendancePresenter @Inject constructor( showErrorView(false) showProgress(true) } - loadData(currentDate, true) + loadData(true) } fun onDetailsClick() { @@ -102,8 +102,8 @@ class AttendancePresenter @Inject constructor( if (view.currentStackSize == 1) { baseDate.also { if (currentDate != it) { - loadData(it) - reloadView() + reloadView(it) + loadData() } else if (!view.isViewEmpty) view.resetView() } } else view.popView() @@ -184,17 +184,30 @@ class AttendancePresenter @Inject constructor( }.launch("holidays") } - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { + private fun loadData(forceRefresh: Boolean = false) { Timber.i("Loading attendance data started") - currentDate = date flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) - attendanceRepository.getAttendance(student, semester, date, date, forceRefresh) + attendanceRepository.getAttendance(student, semester, currentDate, currentDate, forceRefresh) }.onEach { when (it.status) { - Status.LOADING -> view?.showExcuseButton(false) + Status.LOADING -> { + view?.showExcuseButton(false) + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data.let { items -> + if (prefRepository.isShowPresent) items + else items.filter { item -> !item.presence } + }.sortedBy { item -> item.number }) + } + } + } Status.SUCCESS -> { Timber.i("Loading attendance result: Success") view?.apply { @@ -220,7 +233,7 @@ class AttendancePresenter @Inject constructor( } }.afterLoading { view?.run { - hideRefresh() + showRefresh(false) showProgress(false) enableSwipe(true) } @@ -250,12 +263,12 @@ class AttendancePresenter @Inject constructor( showContent(true) showProgress(false) } - loadData(currentDate, forceRefresh = true) + loadData(forceRefresh = true) } Status.ERROR -> { Timber.i("Excusing for absence result: An exception occurred") errorHandler.dispatch(it.error!!) - loadData(currentDate) + loadData() } } }.launch("excuse") @@ -272,11 +285,14 @@ class AttendancePresenter @Inject constructor( } } - private fun reloadView() { + private fun reloadView(date: LocalDate) { + currentDate = date + Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}") view?.apply { showProgress(true) enableSwipe(false) + showRefresh(false) showContent(false) showEmpty(false) showErrorView(false) 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 d54fb8bf1..0459dfcf6 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 @@ -24,7 +24,7 @@ interface AttendanceView : BaseView { fun clearData() - fun hideRefresh() + fun showRefresh(show: Boolean) fun resetView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt index 2f8622374..6b971f26a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt @@ -121,8 +121,8 @@ class AttendanceSummaryFragment : binding.attendanceSummarySubjectsContainer.visibility = if (show) VISIBLE else INVISIBLE } - override fun hideRefresh() { - binding.attendanceSummarySwipe.isRefreshing = false + override fun showRefresh(show: Boolean) { + binding.attendanceSummarySwipe.isRefreshing = show } override fun onSaveInstanceState(outState: Bundle) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt index e5dce9ac8..e53cda749 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt @@ -1,14 +1,15 @@ package io.github.wulkanowy.ui.modules.attendance.summary import io.github.wulkanowy.data.Status +import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.Subject -import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.subject.SubjectRepository +import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.SubjectRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -22,7 +23,7 @@ class AttendanceSummaryPresenter @Inject constructor( private val attendanceSummaryRepository: AttendanceSummaryRepository, private val subjectRepository: SubjectRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var subjects = emptyList() @@ -74,6 +75,8 @@ class AttendanceSummaryPresenter @Inject constructor( } private fun loadData(subjectId: Int, forceRefresh: Boolean = false) { + Timber.i("Loading attendance summary data started") + currentSubjectId = subjectId flowWithResourceIn { @@ -82,15 +85,23 @@ class AttendanceSummaryPresenter @Inject constructor( attendanceSummaryRepository.getAttendanceSummary(student, semester, subjectId, forceRefresh) }.onEach { when (it.status) { - Status.LOADING -> Timber.i("Loading attendance summary data started") + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateDataSet(sortItems(it.data)) + } + } + } Status.SUCCESS -> { Timber.i("Loading attendance summary result: Success") view?.apply { showEmpty(it.data!!.isEmpty()) showContent(it.data.isNotEmpty()) - updateDataSet(it.data.sortedByDescending { item -> - if (item.month.value <= Month.JUNE.value) item.month.value + 12 else item.month.value - }) + updateDataSet(sortItems(it.data)) } analytics.logEvent( "load_data", @@ -106,13 +117,17 @@ class AttendanceSummaryPresenter @Inject constructor( } }.afterLoading { view?.run { - hideRefresh() + showRefresh(false) showProgress(false) enableSwipe(true) } }.launch() } + private fun sortItems(items: List) = items.sortedByDescending { item -> + if (item.month.value <= Month.JUNE.value) item.month.value + 12 else item.month.value + } + private fun showErrorViewOnError(message: String, error: Throwable) { view?.run { if (isViewEmpty) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt index dd4053c72..66f370c5c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt @@ -9,7 +9,7 @@ interface AttendanceSummaryView : BaseView { fun initView() - fun hideRefresh() + fun showRefresh(show: Boolean) fun showContent(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt new file mode 100644 index 000000000..c87286149 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt @@ -0,0 +1,36 @@ +package io.github.wulkanowy.ui.modules.conference + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.databinding.ItemConferenceBinding +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class ConferenceAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemConferenceBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + with(holder.binding) { + conferenceItemDate.text = item.date.toFormattedString("dd.MM.yyyy HH:mm") + conferenceItemName.text = item.presentOnConference + conferenceItemTitle.text = item.title + conferenceItemSubject.text = item.subject + conferenceItemContent.text = item.agenda + conferenceItemContent.visibility = if (item.agenda.isBlank()) View.GONE else View.VISIBLE + } + } + + class ItemViewHolder(val binding: ItemConferenceBinding) : RecyclerView.ViewHolder(binding.root) +} 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 new file mode 100644 index 000000000..6173c15be --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt @@ -0,0 +1,102 @@ +package io.github.wulkanowy.ui.modules.conference + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.databinding.FragmentConferenceBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import javax.inject.Inject + +@AndroidEntryPoint +class ConferenceFragment : BaseFragment(R.layout.fragment_conference), + ConferenceView, MainView.TitledView { + + @Inject + lateinit var presenter: ConferencePresenter + + @Inject + lateinit var conferencesAdapter: ConferenceAdapter + + companion object { + fun newInstance() = ConferenceFragment() + } + + override val isViewEmpty: Boolean + get() = conferencesAdapter.items.isEmpty() + + override val titleStringId: Int + get() = R.string.conferences_title + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentConferenceBinding.bind(view) + messageContainer = binding.conferenceRecycler + presenter.onAttachView(this) + } + + override fun initView() { + with(binding.conferenceRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = conferencesAdapter + addItemDecoration(DividerItemDecoration(context)) + } + + with(binding) { + conferenceSwipe.setOnRefreshListener { presenter.onSwipeRefresh() } + conferenceErrorRetry.setOnClickListener { presenter.onRetry() } + conferenceErrorDetails.setOnClickListener { presenter.onDetailsClick() } + } + } + + override fun updateData(data: List) { + with(conferencesAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun clearData() { + with(conferencesAdapter) { + items = emptyList() + notifyDataSetChanged() + } + } + + override fun showRefresh(show: Boolean) { + binding.conferenceSwipe.isRefreshing = show + } + + override fun showProgress(show: Boolean) { + binding.conferenceProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showEmpty(show: Boolean) { + binding.conferenceEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showErrorView(show: Boolean) { + binding.conferenceError.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun setErrorDetails(message: String) { + binding.conferenceErrorMessage.text = message + } + + override fun enableSwipe(enable: Boolean) { + binding.conferenceSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.conferenceRecycler.visibility = if (show) View.VISIBLE else View.GONE + } + + 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 new file mode 100644 index 000000000..cc7e50db5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt @@ -0,0 +1,108 @@ +package io.github.wulkanowy.ui.modules.conference + +import io.github.wulkanowy.data.Status +import io.github.wulkanowy.data.repositories.ConferenceRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import javax.inject.Inject + +class ConferencePresenter @Inject constructor( + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val semesterRepository: SemesterRepository, + private val conferenceRepository: ConferenceRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private lateinit var lastError: Throwable + + override fun onAttachView(view: ConferenceView) { + super.onAttachView(view) + view.initView() + Timber.i("Conferences view was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData() + } + + fun onSwipeRefresh() { + loadData(true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(true) + } + + fun onDetailsClick() { + view?.showErrorDetailsDialog(lastError) + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading conference data started") + + flowWithResourceIn { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + conferenceRepository.getConferences(student, semester, forceRefresh) + }.onEach { + when (it.status) { + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data.sortedByDescending { conference -> conference.date }) + } + } + } + Status.SUCCESS -> { + Timber.i("Loading conference result: Success") + view?.run { + updateData(it.data!!.sortedByDescending { conference -> conference.date }) + showContent(it.data.isNotEmpty()) + showEmpty(it.data.isEmpty()) + showErrorView(false) + } + analytics.logEvent( + "load_data", + "type" to "conferences", + "items" to it.data!!.size + ) + } + Status.ERROR -> { + Timber.i("Loading conference result: An exception occurred") + errorHandler.dispatch(it.error!!) + } + } + }.afterLoading { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + } + }.launch() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt new file mode 100644 index 000000000..f3d1b3b3f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.ui.modules.conference + +import io.github.wulkanowy.data.db.entities.Conference +import io.github.wulkanowy.ui.base.BaseView + +interface ConferenceView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun clearData() + + fun showRefresh(show: Boolean) + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) +} 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 000da00f8..9cc1aeda3 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 @@ -65,8 +65,8 @@ class ExamFragment : BaseFragment(R.layout.fragment_exam), } } - override fun hideRefresh() { - binding.examSwipe.isRefreshing = false + override fun showRefresh(show: Boolean) { + binding.examSwipe.isRefreshing = show } override fun updateData(data: List>) { 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 f63316a8d..b70a648f4 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 @@ -2,12 +2,12 @@ package io.github.wulkanowy.ui.modules.exam import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Exam -import io.github.wulkanowy.data.repositories.exam.ExamRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.ExamRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday @@ -30,7 +30,7 @@ class ExamPresenter @Inject constructor( studentRepository: StudentRepository, private val examRepository: ExamRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var baseDate: LocalDate = now().nextOrSameSchoolDay @@ -45,24 +45,24 @@ class ExamPresenter @Inject constructor( view.initView() Timber.i("Exam view was initialized") errorHandler.showErrorMessage = ::showErrorViewOnError - loadData(ofEpochDay(date ?: baseDate.toEpochDay())) + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) + loadData() if (currentDate.isHolidays) setBaseDateOnHolidays() - reloadView() } fun onPreviousWeek() { - loadData(currentDate.minusDays(7)) - reloadView() + reloadView(currentDate.minusDays(7)) + loadData() } fun onNextWeek() { - loadData(currentDate.plusDays(7)) - reloadView() + reloadView(currentDate.plusDays(7)) + loadData() } fun onSwipeRefresh() { Timber.i("Force refreshing the exam") - loadData(currentDate, true) + loadData(true) } fun onRetry() { @@ -70,7 +70,7 @@ class ExamPresenter @Inject constructor( showErrorView(false) showProgress(true) } - loadData(currentDate, true) + loadData(true) } fun onDetailsClick() { @@ -86,8 +86,8 @@ class ExamPresenter @Inject constructor( Timber.i("Exam view is reselected") baseDate.also { if (currentDate != it) { - loadData(it) - reloadView() + reloadView(it) + loadData() } else if (view?.isViewEmpty == false) view?.resetView() } } @@ -105,8 +105,8 @@ class ExamPresenter @Inject constructor( }.launch("holidays") } - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - currentDate = date + private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading exam data started") flowWithResourceIn { val student = studentRepository.getCurrentStudent() @@ -114,7 +114,17 @@ class ExamPresenter @Inject constructor( examRepository.getExams(student, semester, currentDate.monday, currentDate.sunday, forceRefresh) }.onEach { when (it.status) { - Status.LOADING -> Timber.i("Loading exam data started") + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(createExamItems(it.data)) + } + } + } Status.SUCCESS -> { Timber.i("Loading exam result: Success") view?.apply { @@ -136,7 +146,7 @@ class ExamPresenter @Inject constructor( } }.afterLoading { view?.run { - hideRefresh() + showRefresh(false) showProgress(false) enableSwipe(true) } @@ -162,7 +172,9 @@ class ExamPresenter @Inject constructor( }.flatten() } - private fun reloadView() { + private fun reloadView(date: LocalDate) { + currentDate = date + Timber.i("Reload exam view with the date ${currentDate.toFormattedString()}") view?.apply { showProgress(true) 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 00429bae6..ac1a87fe9 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 @@ -15,7 +15,7 @@ interface ExamView : BaseView { fun clearData() - fun hideRefresh() + fun showRefresh(show: Boolean) 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 5d0a310f2..0bd971ecc 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt @@ -1,14 +1,13 @@ package io.github.wulkanowy.ui.modules.grade import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.grade.GradeRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository +import io.github.wulkanowy.data.repositories.GradeRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.ALL_YEAR import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.BOTH_SEMESTERS @@ -18,10 +17,8 @@ import io.github.wulkanowy.utils.changeModifier import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.flatMapConcat -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import javax.inject.Inject @@ -50,20 +47,20 @@ class GradeAverageProvider @Inject constructor( val selectedSemester = semesters.single { it.semesterId == semesterId } val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 } - return getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh).flatMapConcat { selectedDetails -> - val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 } + val selectedSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh) - if (selectedSemester != firstSemester) { - getSemesterDetailsWithAverage(student, firstSemester, forceRefresh).map { secondDetails -> - secondDetails.copy(data = selectedDetails.data?.map { selected -> - val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject } - selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) { - val selectedGrades = selected.grades.updateModifiers(student).calcAverage() - (selectedGrades + (second?.grades?.updateModifiers(student)?.calcAverage() ?: selectedGrades)) / 2 - } else (selected.average + (second?.average ?: selected.average)) / 2) - }) - }.filter { it.status != Status.LOADING }.filter { it.data != null } - } else flowOf(selectedDetails) + return if (selectedSemester == firstSemester) selectedSemesterDetailsWithAverage else { + val firstSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, firstSemester, forceRefresh) + selectedSemesterDetailsWithAverage.combine(firstSemesterDetailsWithAverage) { selectedDetails, secondDetails -> + val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 } + secondDetails.copy(data = selectedDetails.data?.map { selected -> + val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject } + selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) { + val selectedGrades = selected.grades.updateModifiers(student).calcAverage() + (selectedGrades + (second?.grades?.updateModifiers(student)?.calcAverage() ?: selectedGrades)) / 2 + } else (selected.average + (second?.average ?: selected.average)) / 2) + }) + } } } @@ -71,19 +68,19 @@ class GradeAverageProvider @Inject constructor( val selectedSemester = semesters.single { it.semesterId == semesterId } val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 } - return getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh).flatMapConcat { selectedDetails -> - val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 } + val selectedSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh) - if (selectedSemester != firstSemester) { - getSemesterDetailsWithAverage(student, firstSemester, forceRefresh).map { secondDetails -> - secondDetails.copy(data = selectedDetails.data?.map { selected -> - val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject } - selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) { - (selected.grades.updateModifiers(student) + second?.grades?.updateModifiers(student).orEmpty()).calcAverage() - } else selected.average) - }) - }.filter { it.status != Status.LOADING }.filter { it.data != null } - } else flowOf(selectedDetails) + return if (selectedSemester == firstSemester) selectedSemesterDetailsWithAverage else { + val firstSemesterDetailsWithAverage = getSemesterDetailsWithAverage(student, firstSemester, forceRefresh) + selectedSemesterDetailsWithAverage.combine(firstSemesterDetailsWithAverage) { selectedDetails, secondDetails -> + val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 } + secondDetails.copy(data = selectedDetails.data?.map { selected -> + val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject } + selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) { + (selected.grades.updateModifiers(student) + second?.grades?.updateModifiers(student).orEmpty()).calcAverage() + } else selected.average) + }) + } } } @@ -93,7 +90,7 @@ class GradeAverageProvider @Inject constructor( val isAnyAverage = summaries.orEmpty().any { it.average != .0 } val allGrades = details.orEmpty().groupBy { it.subject } - Resource(res.status, summaries?.emulateEmptySummaries(student, semester, allGrades.toList(), isAnyAverage)?.map { summary -> + val items = summaries?.emulateEmptySummaries(student, semester, allGrades.toList(), isAnyAverage)?.map { summary -> val grades = allGrades[summary.subject].orEmpty() GradeDetailsWithAverage( subject = summary.subject, @@ -104,7 +101,9 @@ class GradeAverageProvider @Inject constructor( summary = summary, grades = grades ) - }, res.error) + } + + Resource(res.status, items, res.error) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt index d4c9f210a..d64613c01 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt @@ -2,11 +2,11 @@ package io.github.wulkanowy.ui.modules.grade import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.getCurrentOrLast import kotlinx.coroutines.delay @@ -18,7 +18,7 @@ class GradePresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { var selectedIndex = 0 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 c129d9485..6f9321692 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 @@ -40,10 +40,6 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter -) +) { + var newGrades = 0 +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index 97b7b71c6..afec6b5ee 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -3,17 +3,17 @@ package io.github.wulkanowy.ui.modules.grade.details import android.annotation.SuppressLint import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Grade -import io.github.wulkanowy.data.repositories.grade.GradeRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.GradeRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.ALPHABETIC import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.DATE -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResourceIn @@ -29,7 +29,7 @@ class GradeDetailsPresenter @Inject constructor( private val semesterRepository: SemesterRepository, private val preferencesRepository: PreferencesRepository, private val averageProvider: GradeAverageProvider, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var newGradesAmount: Int = 0 @@ -81,7 +81,10 @@ class GradeDetailsPresenter @Inject constructor( }.onEach { when (it.status) { Status.LOADING -> Timber.i("Select mark grades as read") - Status.SUCCESS -> Timber.i("Mark as read result: Success") + Status.SUCCESS -> { + Timber.i("Mark as read result: Success") + loadData(currentSemesterId, false) + } Status.ERROR -> { Timber.i("Mark as read result: An exception occurred") errorHandler.dispatch(it.error!!) @@ -134,15 +137,37 @@ class GradeDetailsPresenter @Inject constructor( } private fun loadData(semesterId: Int, forceRefresh: Boolean) { + Timber.i("Loading grade details data started") + flowWithResourceIn { val student = studentRepository.getCurrentStudent() averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh) }.onEach { + Timber.d("Loading grade details status: ${it.status}, data: ${it.data != null}") when (it.status) { - Status.LOADING -> Timber.i("Loading grade details data started") + Status.LOADING -> { + val items = createGradeItems(it.data.orEmpty()) + if (items.isNotEmpty()) { + Timber.i("Loading grade details result: load cached data") + view?.run { + updateNewGradesAmount(it.data.orEmpty()) + enableSwipe(true) + showRefresh(true) + showProgress(false) + showEmpty(false) + showContent(true) + updateData( + data = items, + isGradeExpandable = preferencesRepository.isGradeExpandable, + gradeColorTheme = preferencesRepository.gradeColorTheme + ) + notifyParentDataLoaded(semesterId) + } + } + } Status.SUCCESS -> { Timber.i("Loading grade details result: Success") - newGradesAmount = it.data!!.sumBy { item -> item.grades.sumBy { grade -> if (!grade.isRead) 1 else 0 } } + updateNewGradesAmount(it.data!!) updateMarkAsDoneButton() val items = createGradeItems(it.data) view?.run { @@ -176,6 +201,10 @@ class GradeDetailsPresenter @Inject constructor( }.launch() } + private fun updateNewGradesAmount(grades: List) { + newGradesAmount = grades.sumBy { item -> item.grades.sumBy { grade -> if (!grade.isRead) 1 else 0 } } + } + private fun showErrorViewOnError(message: String, error: Throwable) { view?.run { if (isViewEmpty) { @@ -210,9 +239,10 @@ class GradeDetailsPresenter @Inject constructor( subject = subject, average = average, pointsSum = points, - newGrades = grades.filter { grade -> !grade.isRead }.size, grades = subItems - ), ViewType.HEADER)) + if (preferencesRepository.isGradeExpandable) emptyList() else subItems + ).apply { + newGrades = grades.filter { grade -> !grade.isRead }.size + }, ViewType.HEADER)) + if (preferencesRepository.isGradeExpandable) emptyList() else subItems }.flatten() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt index dbb60910a..cbcb444a1 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 @@ -17,8 +17,9 @@ import com.github.mikephil.charting.data.PieDataSet import com.github.mikephil.charting.data.PieEntry import com.github.mikephil.charting.formatter.ValueFormatter import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.GradePartialStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics -import io.github.wulkanowy.data.db.entities.GradeStatistics +import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics import io.github.wulkanowy.data.pojos.GradeStatisticsItem import io.github.wulkanowy.databinding.ItemGradeStatisticsBarBinding import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding @@ -68,22 +69,32 @@ class GradeStatisticsAdapter @Inject constructor() : override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val inflater = LayoutInflater.from(parent.context) return when (viewType) { - ViewType.PARTIAL.id, ViewType.SEMESTER.id -> PieViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false)) - ViewType.POINTS.id -> BarViewHolder(ItemGradeStatisticsBarBinding.inflate(inflater, parent, false)) + ViewType.PARTIAL.id -> PartialViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false)) + ViewType.SEMESTER.id -> SemesterViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false)) + ViewType.POINTS.id -> PointsViewHolder(ItemGradeStatisticsBarBinding.inflate(inflater, parent, false)) else -> throw IllegalStateException() } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { - is PieViewHolder -> bindPieChart(holder, items[position].partial) - is BarViewHolder -> bindBarChart(holder, items[position].points!!) + is PartialViewHolder -> bindPartialChart(holder, items[position].partial!!) + is SemesterViewHolder -> bindSemesterChart(holder, items[position].semester!!) + is PointsViewHolder -> bindBarChart(holder, items[position].points!!) } } - private fun bindPieChart(holder: PieViewHolder, partials: List) { - with(holder.binding.gradeStatisticsPieTitle) { - text = partials.firstOrNull()?.subject + private fun bindPartialChart(holder: PartialViewHolder, partials: GradePartialStatistics) { + bindPieChart(holder.binding, partials.subject, partials.classAverage, partials.classAmounts) + } + + private fun bindSemesterChart(holder: SemesterViewHolder, semester: GradeSemesterStatistics) { + bindPieChart(holder.binding, semester.subject, semester.average, semester.amounts) + } + + private fun bindPieChart(binding: ItemGradeStatisticsPieBinding, subject: String, average: String, amounts: List) { + with(binding.gradeStatisticsPieTitle) { + text = subject visibility = if (items.size == 1 || !showAllSubjectsOnList) GONE else VISIBLE } @@ -92,22 +103,23 @@ class GradeStatisticsAdapter @Inject constructor() : else -> materialGradeColors } - val dataset = PieDataSet(partials.map { - PieEntry(it.amount.toFloat(), it.grade.toString()) - }, "Legenda") + val dataset = PieDataSet(amounts.mapIndexed { grade, amount -> + PieEntry(amount.toFloat(), (grade + 1).toString()) + }.reversed().filterNot { it.value == 0f }, "Legenda") with(dataset) { valueTextSize = 12f sliceSpace = 1f valueTextColor = Color.WHITE - setColors(partials.map { - gradeColors.single { color -> color.first == it.grade }.second - }.toIntArray(), holder.binding.root.context) + val grades = amounts.mapIndexed { grade, amount -> (grade + 1) to amount }.filterNot { it.second == 0 } + setColors(grades.reversed().map { (grade, _) -> + gradeColors.single { color -> color.first == grade }.second + }.toIntArray(), binding.root.context) } - with(holder.binding.gradeStatisticsPie) { + with(binding.gradeStatisticsPie) { setTouchEnabled(false) - if (partials.size == 1) animateXY(1000, 1000) + if (amounts.size == 1) animateXY(1000, 1000) data = PieData(dataset).apply { setValueFormatter(object : ValueFormatter() { override fun getPieLabel(value: Float, pieEntry: PieEntry): String { @@ -128,8 +140,9 @@ class GradeStatisticsAdapter @Inject constructor() : minAngleForSlices = 25f description.isEnabled = false - centerText = partials.fold(0) { acc, it -> acc + it.amount } - .let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } + centerText = amounts.fold(0) { acc, it -> acc + it } + .let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } + + ("\n\nŚrednia: $average").takeIf { average.isNotBlank() }.orEmpty() setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground)) setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary)) @@ -137,7 +150,7 @@ class GradeStatisticsAdapter @Inject constructor() : } } - private fun bindBarChart(holder: BarViewHolder, points: GradePointsStatistics) { + private fun bindBarChart(holder: PointsViewHolder, points: GradePointsStatistics) { with(holder.binding.gradeStatisticsBarTitle) { text = points.subject visibility = if (items.size == 1) GONE else VISIBLE @@ -200,9 +213,12 @@ class GradeStatisticsAdapter @Inject constructor() : } } - private class PieViewHolder(val binding: ItemGradeStatisticsPieBinding) : + private class PartialViewHolder(val binding: ItemGradeStatisticsPieBinding) : RecyclerView.ViewHolder(binding.root) - private class BarViewHolder(val binding: ItemGradeStatisticsBarBinding) : + private class SemesterViewHolder(val binding: ItemGradeStatisticsPieBinding) : + RecyclerView.ViewHolder(binding.root) + + private class PointsViewHolder(val binding: ItemGradeStatisticsBarBinding) : RecyclerView.ViewHolder(binding.root) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt index 73cee9e96..37f478693 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt @@ -2,14 +2,15 @@ package io.github.wulkanowy.ui.modules.grade.statistics import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Subject -import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.subject.SubjectRepository +import io.github.wulkanowy.data.pojos.GradeStatisticsItem +import io.github.wulkanowy.data.repositories.GradeStatisticsRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.SubjectRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -23,7 +24,7 @@ class GradeStatisticsPresenter @Inject constructor( private val subjectRepository: SubjectRepository, private val semesterRepository: SemesterRepository, private val preferencesRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var subjects = emptyList() @@ -143,6 +144,8 @@ class GradeStatisticsPresenter @Inject constructor( } private fun loadDataByType(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean = false) { + Timber.i("Loading grade stats data started") + currentSubjectName = if (preferencesRepository.showAllSubjectsOnStatisticsList) "Wszystkie" else subjectName currentType = type @@ -153,19 +156,33 @@ class GradeStatisticsPresenter @Inject constructor( with(gradeStatisticsRepository) { when (type) { - ViewType.SEMESTER -> getGradesStatistics(student, semester, currentSubjectName, true, forceRefresh) - ViewType.PARTIAL -> getGradesStatistics(student, semester, currentSubjectName, false, forceRefresh) + ViewType.PARTIAL -> getGradesPartialStatistics(student, semester, currentSubjectName, forceRefresh) + ViewType.SEMESTER -> getGradesSemesterStatistics(student, semester, currentSubjectName, forceRefresh) ViewType.POINTS -> getGradesPointsStatistics(student, semester, currentSubjectName, forceRefresh) } } }.onEach { when (it.status) { - Status.LOADING -> Timber.i("Loading grade stats data started") + Status.LOADING -> { + val isNoContent = it.data == null || checkIsNoContent(it.data, type) + if (!isNoContent) { + view?.run { + showEmpty(isNoContent) + showContent(!isNoContent) + showErrorView(false) + enableSwipe(true) + showRefresh(true) + updateData(it.data!!, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList) + showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) + } + } + } Status.SUCCESS -> { Timber.i("Loading grade stats result: Success") view?.run { - showEmpty(it.data!!.isEmpty() || it.data.first().partial.isEmpty()) - showContent(it.data.isNotEmpty() && it.data.first().partial.isNotEmpty()) + val isNoContent = checkIsNoContent(it.data!!, type) + showEmpty(isNoContent) + showContent(!isNoContent) showErrorView(false) updateData(it.data, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList) showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) @@ -191,6 +208,16 @@ class GradeStatisticsPresenter @Inject constructor( }.launch("load") } + private fun checkIsNoContent(items: List, type: ViewType): Boolean { + return items.isEmpty() || when (type) { + ViewType.SEMESTER -> items.firstOrNull()?.semester?.amounts.orEmpty().sum() == 0 + ViewType.PARTIAL -> items.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0 + ViewType.POINTS -> items.firstOrNull()?.points?.let { points -> + points.student == .0 && points.others == .0 + } ?: false + } + } + private fun showErrorViewOnError(message: String, error: Throwable) { view?.run { if (isViewEmpty) { 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 caa3cb84d..17c14b85c 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 @@ -2,12 +2,12 @@ package io.github.wulkanowy.ui.modules.grade.summary import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.GradeSummary -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -18,7 +18,7 @@ class GradeSummaryPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val averageProvider: GradeAverageProvider, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private lateinit var lastError: Throwable @@ -37,12 +37,28 @@ class GradeSummaryPresenter @Inject constructor( } private fun loadData(semesterId: Int, forceRefresh: Boolean) { + Timber.i("Loading grade summary started") + flowWithResourceIn { val student = studentRepository.getCurrentStudent() averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh) }.onEach { + Timber.d("Loading grade summary status: ${it.status}, data: ${it.data != null}") when (it.status) { - Status.LOADING -> Timber.i("Loading grade summary started") + Status.LOADING -> { + val items = createGradeSummaryItems(it.data.orEmpty()) + if (items.isNotEmpty()) { + Timber.i("Loading grade summary result: load cached data") + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showEmpty(false) + showContent(true) + updateData(items) + } + } + } Status.SUCCESS -> { Timber.i("Loading grade summary result: Success") val items = createGradeSummaryItems(it.data!!) 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 e1d7ac52f..85173e91d 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 @@ -83,8 +83,8 @@ class HomeworkFragment : BaseFragment(R.layout.fragment binding.homeworkNavDate.text = date } - override fun hideRefresh() { - binding.homeworkSwipe.isRefreshing = false + override fun showRefresh(show: Boolean) { + binding.homeworkSwipe.isRefreshing = show } override fun showEmpty(show: Boolean) { 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 8af0d83f6..11c54dc24 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt @@ -2,12 +2,12 @@ package io.github.wulkanowy.ui.modules.homework import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.repositories.homework.HomeworkRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.HomeworkRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday @@ -29,7 +29,7 @@ class HomeworkPresenter @Inject constructor( studentRepository: StudentRepository, private val homeworkRepository: HomeworkRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var baseDate: LocalDate = LocalDate.now().nextOrSameSchoolDay @@ -44,24 +44,24 @@ class HomeworkPresenter @Inject constructor( view.initView() Timber.i("Homework view was initialized") errorHandler.showErrorMessage = ::showErrorViewOnError - loadData(ofEpochDay(date ?: baseDate.toEpochDay())) + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) + loadData() if (currentDate.isHolidays) setBaseDateOnHolidays() - reloadView() } fun onPreviousDay() { - loadData(currentDate.minusDays(7)) - reloadView() + reloadView(currentDate.minusDays(7)) + loadData() } fun onNextDay() { - loadData(currentDate.plusDays(7)) - reloadView() + reloadView(currentDate.plusDays(7)) + loadData() } fun onSwipeRefresh() { Timber.i("Force refreshing the homework") - loadData(currentDate, true) + loadData(true) } fun onRetry() { @@ -69,7 +69,7 @@ class HomeworkPresenter @Inject constructor( showErrorView(false) showProgress(true) } - loadData(currentDate, true) + loadData(true) } fun onDetailsClick() { @@ -94,16 +94,26 @@ class HomeworkPresenter @Inject constructor( }.launch("holidays") } - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - currentDate = date + private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading homework data started") flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) - homeworkRepository.getHomework(student, semester, date, date, forceRefresh) + homeworkRepository.getHomework(student, semester, currentDate, currentDate, forceRefresh) }.onEach { when (it.status) { - Status.LOADING -> Timber.i("Loading homework data started") + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(createHomeworkItem(it.data)) + } + } + } Status.SUCCESS -> { Timber.i("Loading homework result: Success") view?.apply { @@ -125,7 +135,7 @@ class HomeworkPresenter @Inject constructor( } }.afterLoading { view?.run { - hideRefresh() + showRefresh(false) showProgress(false) enableSwipe(true) } @@ -151,7 +161,9 @@ class HomeworkPresenter @Inject constructor( }.flatten() } - private fun reloadView() { + private fun reloadView(date: LocalDate) { + currentDate = date + Timber.i("Reload homework view with the date ${currentDate.toFormattedString()}") view?.apply { showProgress(true) 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 2a678cd4c..a1d6a04a9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt @@ -15,7 +15,7 @@ interface HomeworkView : BaseView { fun updateNavigationWeek(date: String) - fun hideRefresh() + fun showRefresh(show: Boolean) fun showEmpty(show: Boolean) 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 e485dd748..ca6fc71ee 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt @@ -2,12 +2,12 @@ package io.github.wulkanowy.ui.modules.homework.details import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.data.repositories.homework.HomeworkRepository -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +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.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.flowWithResource import kotlinx.coroutines.flow.onEach import timber.log.Timber @@ -17,7 +17,7 @@ class HomeworkDetailsPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val homeworkRepository: HomeworkRepository, - private val analytics: FirebaseAnalyticsHelper, + private val analytics: AnalyticsHelper, private val preferencesRepository: PreferencesRepository ) : BasePresenter(errorHandler, studentRepository) { 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 749989e3a..aff1c84ca 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 @@ -14,6 +14,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.utils.UpdateHelper import io.github.wulkanowy.utils.setOnSelectPageListener import javax.inject.Inject @@ -25,6 +26,9 @@ class LoginActivity : BaseActivity(), Logi private val loginAdapter = BaseFragmentPagerAdapter(supportFragmentManager) + @Inject + lateinit var updateHelper: UpdateHelper + companion object { fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java) @@ -37,8 +41,20 @@ class LoginActivity : BaseActivity(), Logi setContentView(ActivityLoginBinding.inflate(layoutInflater).apply { binding = this }.root) setSupportActionBar(binding.loginToolbar) messageContainer = binding.loginContainer + updateHelper.messageContainer = binding.loginContainer presenter.onAttachView(this) + updateHelper.checkAndInstallUpdates(this) + } + + override fun onResume() { + super.onResume() + updateHelper.onResume(this) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + updateHelper.onActivityResult(requestCode, resultCode) } override fun initView() { 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 f30825cff..ed4563246 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt @@ -2,7 +2,6 @@ package io.github.wulkanowy.ui.modules.login import android.content.res.Resources import android.database.sqlite.SQLiteConstraintException -import com.chuckerteam.chucker.api.ChuckerCollector import io.github.wulkanowy.R import io.github.wulkanowy.sdk.mobile.exception.InvalidPinException import io.github.wulkanowy.sdk.mobile.exception.InvalidSymbolException @@ -12,10 +11,7 @@ import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject -class LoginErrorHandler @Inject constructor( - resources: Resources, - chuckerCollector: ChuckerCollector -) : ErrorHandler(resources, chuckerCollector) { +class LoginErrorHandler @Inject constructor(resources: Resources) : ErrorHandler(resources) { var onBadCredentials: () -> Unit = {} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt index 7e731772f..aa1e7eced 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt @@ -1,7 +1,7 @@ package io.github.wulkanowy.ui.modules.login import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.data.repositories.student.StudentRepository +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 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 dfe82ad8b..891a6b0bb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt @@ -2,11 +2,11 @@ package io.github.wulkanowy.ui.modules.login.advanced import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank @@ -17,7 +17,7 @@ import javax.inject.Inject class LoginAdvancedPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { override fun onAttachView(view: LoginAdvancedView) { 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 41345686a..c6d0209da 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 @@ -89,6 +89,8 @@ class LoginFormFragment : BaseFragment(R.layout.fragme } } + override fun getHostsValues(): List = hostValues.toList() + override fun setCredentials(username: String, pass: String) { with(binding) { loginFormUsername.setText(username) @@ -96,6 +98,12 @@ class LoginFormFragment : BaseFragment(R.layout.fragme } } + override fun setHost(host: String) { + binding.loginFormHost.setText( + hostKeys.getOrNull(hostValues.indexOf(host)).orEmpty() + ) + } + override fun setUsernameLabel(label: String) { binding.loginFormUsernameLayout.hint = label } 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 b921140c7..f6a528ae3 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,10 +1,11 @@ package io.github.wulkanowy.ui.modules.login.form +import androidx.core.net.toUri import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank @@ -15,7 +16,7 @@ import javax.inject.Inject class LoginFormPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { private var lastError: Throwable? = null @@ -56,7 +57,7 @@ class LoginFormPresenter @Inject constructor( fun updateUsernameLabel() { view?.run { - setUsernameLabel(if ("standard" in formHostValue) emailLabel else nicknameLabel) + setUsernameLabel(if ("email" !in formHostValue) nicknameLabel else emailLabel) } } @@ -66,6 +67,16 @@ class LoginFormPresenter @Inject constructor( fun onUsernameTextChanged() { view?.clearUsernameError() + + val username = view?.formUsernameValue.orEmpty().trim() + if ("@" in username && "@vulcan" !in username) { + val hosts = view?.getHostsValues().orEmpty().map { it.toUri().host to it }.toMap() + val usernameHost = username.substringAfter("@") + + hosts[usernameHost]?.let { + view?.setHost(it) + } + } } fun onSignInClick() { @@ -135,12 +146,12 @@ class LoginFormPresenter @Inject constructor( view?.setErrorUsernameRequired() isCorrect = false } else { - if ("@" in login && "standard" !in host) { + if ("@" in login && "login" in host) { view?.setErrorLoginRequired() isCorrect = false } - if ("@" !in login && "standard" in host) { + if ("@" !in login && "email" in host) { view?.setErrorEmailRequired() isCorrect = false } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt index 77e665389..31f8a6217 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 @@ -19,8 +19,12 @@ interface LoginFormView : BaseView { val emailLabel: String + fun getHostsValues(): List + fun setCredentials(username: String, pass: String) + fun setHost(host: String) + fun setUsernameLabel(label: String) fun setErrorUsernameRequired() 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 e58377058..271e8a8a0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt @@ -1,10 +1,10 @@ package io.github.wulkanowy.ui.modules.login.recover import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.recover.RecoverRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.RecoverRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank @@ -15,7 +15,7 @@ import javax.inject.Inject class LoginRecoverPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: RecoverErrorHandler, - private val analytics: FirebaseAnalyticsHelper, + private val analytics: AnalyticsHelper, private val recoverRepository: RecoverRepository ) : BasePresenter(loginErrorHandler, studentRepository) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt index 110dd82e5..8619369dd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt @@ -1,17 +1,13 @@ package io.github.wulkanowy.ui.modules.login.recover import android.content.res.Resources -import com.chuckerteam.chucker.api.ChuckerCollector import io.github.wulkanowy.sdk.scrapper.exception.InvalidCaptchaException import io.github.wulkanowy.sdk.scrapper.exception.InvalidEmailException import io.github.wulkanowy.sdk.scrapper.exception.NoAccountFoundException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject -class RecoverErrorHandler @Inject constructor( - resources: Resources, - chuckerCollector: ChuckerCollector -) : ErrorHandler(resources, chuckerCollector) { +class RecoverErrorHandler @Inject constructor(resources: Resources) : ErrorHandler(resources) { var onInvalidUsername: (String) -> Unit = {} 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 1384f25a0..c344bf441 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt @@ -3,10 +3,10 @@ package io.github.wulkanowy.ui.modules.login.studentselect import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentWithSemesters -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank import kotlinx.coroutines.flow.onEach @@ -17,7 +17,7 @@ import javax.inject.Inject class LoginStudentSelectPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { private var lastError: Throwable? = null 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 a2cda4fc1..4593d880a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt @@ -1,10 +1,10 @@ package io.github.wulkanowy.ui.modules.login.symbol import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.ifNullOrBlank @@ -16,7 +16,7 @@ import javax.inject.Inject class LoginSymbolPresenter @Inject constructor( studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository) { private var lastError: Throwable? = null 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 90aea441e..fd0598d8f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt @@ -1,11 +1,11 @@ package io.github.wulkanowy.ui.modules.luckynumber import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.LuckyNumberRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -16,7 +16,7 @@ class LuckyNumberPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val luckyNumberRepository: LuckyNumberRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private lateinit var lastError: Throwable 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 574120b36..f4041e9e4 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 @@ -3,7 +3,7 @@ package io.github.wulkanowy.ui.modules.luckynumberwidget import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.student.StudentRepository +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.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getStudentWidgetKey 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 b7f4b740e..49a199431 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt @@ -16,8 +16,8 @@ import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.exceptions.NoCurrentStudentException -import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.LuckyNumberRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.toFirstResult 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 95b4aa77a..25b41aab5 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 @@ -38,8 +38,9 @@ import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.more.MoreFragment import io.github.wulkanowy.ui.modules.note.NoteFragment import io.github.wulkanowy.ui.modules.timetable.TimetableFragment +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.AppInfo -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.UpdateHelper import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.safelyPopFragments @@ -54,7 +55,10 @@ class MainActivity : BaseActivity(), MainVie override lateinit var presenter: MainPresenter @Inject - lateinit var analytics: FirebaseAnalyticsHelper + lateinit var analytics: AnalyticsHelper + + @Inject + lateinit var updateHelper: UpdateHelper @Inject lateinit var appInfo: AppInfo @@ -100,6 +104,7 @@ class MainActivity : BaseActivity(), MainVie setContentView(ActivityMainBinding.inflate(layoutInflater).apply { binding = this }.root) setSupportActionBar(binding.mainToolbar) messageContainer = binding.mainFragmentContainer + updateHelper.messageContainer = binding.mainFragmentContainer presenter.onAttachView(this, MainView.Section.values().singleOrNull { it.id == intent.getIntExtra(EXTRA_START_MENU, -1) }) @@ -107,6 +112,18 @@ class MainActivity : BaseActivity(), MainVie initialize(startMenuIndex, savedInstanceState) pushFragment(moreMenuFragments[startMenuMoreIndex]) } + updateHelper.checkAndInstallUpdates(this) + } + + override fun onResume() { + super.onResume() + updateHelper.onResume(this) + } + + @SuppressLint("NewApi") + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + updateHelper.onActivityResult(requestCode, resultCode) if (appInfo.systemVersion >= Build.VERSION_CODES.N_MR1) initShortcuts() } @@ -165,7 +182,10 @@ class MainActivity : BaseActivity(), MainVie } with(navController) { - setOnViewChangeListener(presenter::onViewChange) + setOnViewChangeListener { section, name -> + analytics.setCurrentScreen(this@MainActivity, name) + presenter.onViewChange(section) + } fragmentHideStrategy = HIDE rootFragments = listOf( GradeFragment.newInstance(), @@ -177,12 +197,8 @@ class MainActivity : BaseActivity(), MainVie } } - override fun setCurrentScreen(name: String?) { - analytics.setCurrentScreen(this, name) - } - - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - return if (item?.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected() + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected() else false } @@ -191,6 +207,7 @@ class MainActivity : BaseActivity(), MainVie } override fun switchMenuView(position: Int) { + analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName) navController.switchTab(position) } @@ -228,10 +245,12 @@ class MainActivity : BaseActivity(), MainVie } fun pushView(fragment: Fragment) { + analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName) navController.pushFragment(fragment) } override fun popView(depth: Int) { + analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName) navController.safelyPopFragments(depth) } 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 8d5c9d67f..59937b332 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -1,14 +1,14 @@ package io.github.wulkanowy.ui.modules.main -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.modules.main.MainView.Section.GRADE import io.github.wulkanowy.ui.modules.main.MainView.Section.MESSAGE import io.github.wulkanowy.ui.modules.main.MainView.Section.SCHOOL -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import timber.log.Timber import javax.inject.Inject @@ -17,7 +17,7 @@ class MainPresenter @Inject constructor( studentRepository: StudentRepository, private val prefRepository: PreferencesRepository, private val syncManager: SyncManager, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { fun onAttachView(view: MainView, initMenu: MainView.Section?) { @@ -35,9 +35,8 @@ class MainPresenter @Inject constructor( analytics.logEvent("app_open", "destination" to initMenu?.name) } - fun onViewChange(section: MainView.Section?, name: String?) { + fun onViewChange(section: MainView.Section?) { view?.apply { - setCurrentScreen(name) showActionBarElevation(section != GRADE && section != MESSAGE && section != SCHOOL) currentViewTitle?.let { setViewTitle(it) } currentViewSubtitle?.let { setViewSubTitle(it.ifBlank { null }) } 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 7e5831471..97b556e3e 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 @@ -24,8 +24,6 @@ interface MainView : BaseView { fun showAccountPicker() - fun setCurrentScreen(name: String?) - fun showActionBarElevation(show: Boolean) fun notifyMenuViewReselected() 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 16e0184a0..72fc627f8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt @@ -6,9 +6,9 @@ import android.view.View.INVISIBLE import android.view.View.VISIBLE import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED -import io.github.wulkanowy.data.repositories.message.MessageFolder.SENT -import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED +import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED +import io.github.wulkanowy.data.enums.MessageFolder.SENT +import io.github.wulkanowy.data.enums.MessageFolder.TRASHED import io.github.wulkanowy.databinding.FragmentMessageBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter 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 0fb454b31..7b8c3d0f5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.message -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import kotlinx.coroutines.delay 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 740f4927c..74f8f57ec 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -65,6 +65,9 @@ class MessagePreviewFragment : override val printHTML: String get() = requireContext().assets.open("message-print-page.html").bufferedReader().use { it.readText() } + override val messageNotExists: String + get() = getString(R.string.message_not_exists) + companion object { const val MESSAGE_ID_KEY = "message_id" @@ -174,7 +177,7 @@ class MessagePreviewFragment : @RequiresApi(Build.VERSION_CODES.LOLLIPOP) override fun printDocument(html: String, jobName: String) { - val webView = WebView(activity) + val webView = WebView(requireContext()) webView.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false 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 7a633a2df..55b9631af 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 @@ -5,13 +5,13 @@ import android.os.Build import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.MessageAttachment -import io.github.wulkanowy.data.repositories.message.MessageFolder -import io.github.wulkanowy.data.repositories.message.MessageRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.data.repositories.MessageRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.AppInfo -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResourceIn @@ -24,7 +24,7 @@ class MessagePreviewPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, - private val analytics: FirebaseAnalyticsHelper, + private val analytics: AnalyticsHelper, private var appInfo: AppInfo ) : BasePresenter(errorHandler, studentRepository) { @@ -65,18 +65,24 @@ class MessagePreviewPresenter @Inject constructor( Status.LOADING -> Timber.i("Loading message ${message.messageId} preview started") Status.SUCCESS -> { Timber.i("Loading message ${message.messageId} preview result: Success ") - checkNotNull(it.data, { "Can't find message in local db! Probably no longer exist in this folder" }) - this@MessagePreviewPresenter.message = it.data.message - this@MessagePreviewPresenter.attachments = it.data.attachments - view?.apply { - setMessageWithAttachment(it.data) - initOptions() + if (it.data != null) { + this@MessagePreviewPresenter.message = it.data.message + this@MessagePreviewPresenter.attachments = it.data.attachments + view?.apply { + setMessageWithAttachment(it.data) + initOptions() + } + analytics.logEvent( + "load_item", + "type" to "message_preview", + "length" to it.data.message.content.length + ) + } else { + view?.run { + showMessage(messageNotExists) + popView() + } } - analytics.logEvent( - "load_item", - "type" to "message_preview", - "length" to it.data.message.content.length - ) } Status.ERROR -> { Timber.i("Loading message ${message.messageId} preview result: An exception occurred ") 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 fa6d735ed..583ba6878 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -14,6 +14,8 @@ interface MessagePreviewView : BaseView { val printHTML: String + val messageNotExists: String + fun initView() fun setMessageWithAttachment(item: MessageWithAttachment) 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 2267279c2..59944d41d 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,5 +1,6 @@ package io.github.wulkanowy.ui.modules.message.send +import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.graphics.Rect @@ -74,6 +75,7 @@ class SendMessageActivity : BaseActivity(errorHandler, studentRepository) { fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) { @@ -100,7 +100,7 @@ class SendMessagePresenter @Inject constructor( Timber.i("Loading recipients started") val recipients = when { - unit != null -> recipientRepository.getRecipients(student, 2, unit) + unit != null -> recipientRepository.getRecipients(student, unit, 2) else -> listOf() }.let { createChips(it) } Timber.i("Loading recipients result: Success, fetched %d recipients", recipients.size) 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 e9b34da33..f0f65e087 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 @@ -9,7 +9,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.NO_POSITION import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.repositories.message.MessageFolder +import io.github.wulkanowy.data.enums.MessageFolder import io.github.wulkanowy.databinding.ItemMessageBinding import io.github.wulkanowy.utils.toFormattedString import javax.inject.Inject 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 44ad4bf18..48a2cee26 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 @@ -12,7 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.repositories.message.MessageFolder +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 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 79062dcfc..c560a77de 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 @@ -2,13 +2,13 @@ package io.github.wulkanowy.ui.modules.message.tab import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Message -import io.github.wulkanowy.data.repositories.message.MessageFolder -import io.github.wulkanowy.data.repositories.message.MessageRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.enums.MessageFolder +import io.github.wulkanowy.data.repositories.MessageRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.toFormattedString @@ -32,7 +32,7 @@ class MessageTabPresenter @Inject constructor( studentRepository: StudentRepository, private val messageRepository: MessageRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { lateinit var folder: MessageFolder @@ -85,13 +85,27 @@ class MessageTabPresenter @Inject constructor( } private fun loadData(forceRefresh: Boolean) { + Timber.i("Loading $folder message data started") + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) messageRepository.getMessages(student, semester, folder, forceRefresh) }.onEach { when (it.status) { - Status.LOADING -> Timber.i("Loading $folder message data started") + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + messages = it.data + updateData(getFilteredData(lastSearchQuery)) + notifyParentDataLoaded() + } + } + } Status.SUCCESS -> { Timber.i("Loading $folder message result: Success") messages = it.data!! 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 8065e9b60..48737d7ba 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 @@ -100,8 +100,8 @@ class MobileDeviceFragment : } } - override fun hideRefresh() { - binding.mobileDevicesSwipe.isRefreshing = false + override fun showRefresh(show: Boolean) { + binding.mobileDevicesSwipe.isRefreshing = show } override fun showProgress(show: Boolean) { 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 6314c2c31..9591867df 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt @@ -2,12 +2,12 @@ package io.github.wulkanowy.ui.modules.mobiledevice import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.MobileDevice -import io.github.wulkanowy.data.repositories.mobiledevice.MobileDeviceRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.MobileDeviceRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResourceIn @@ -21,7 +21,7 @@ class MobileDevicePresenter @Inject constructor( studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, private val mobileDeviceRepository: MobileDeviceRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private lateinit var lastError: Throwable @@ -51,13 +51,25 @@ class MobileDevicePresenter @Inject constructor( } private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading mobile devices data started") + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) mobileDeviceRepository.getDevices(student, semester, forceRefresh) }.onEach { when (it.status) { - Status.LOADING -> Timber.i("Loading mobile devices data started") + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data) + } + } + } Status.SUCCESS -> { Timber.i("Loading mobile devices result: Success") view?.run { @@ -79,7 +91,7 @@ class MobileDevicePresenter @Inject constructor( } }.afterLoading { view?.run { - hideRefresh() + showRefresh(false) showProgress(false) enableSwipe(true) } 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 ec2d3f87f..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 @@ -17,7 +17,7 @@ interface MobileDeviceView : BaseView { fun showUndo(device: MobileDevice, position: Int) - fun hideRefresh() + fun showRefresh(show: Boolean) fun showProgress(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt index f6d0de992..5e7110ee5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt @@ -1,12 +1,12 @@ package io.github.wulkanowy.ui.modules.mobiledevice.token import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.mobiledevice.MobileDeviceRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.MobileDeviceRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import kotlinx.coroutines.flow.onEach @@ -18,7 +18,7 @@ class MobileDeviceTokenPresenter @Inject constructor( studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, private val mobileDeviceRepository: MobileDeviceRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { override fun onAttachView(view: MobileDeviceTokenVIew) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt index 1bdcc26fa..bf8918fce 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt @@ -9,6 +9,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.databinding.FragmentMoreBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.about.AboutFragment +import io.github.wulkanowy.ui.modules.conference.ConferenceFragment import io.github.wulkanowy.ui.modules.homework.HomeworkFragment import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment import io.github.wulkanowy.ui.modules.main.MainActivity @@ -53,6 +54,9 @@ class MoreFragment : BaseFragment(R.layout.fragment_more), override val mobileDevicesRes: Pair? get() = context?.run { getString(R.string.mobile_devices_title) to getCompatDrawable(R.drawable.ic_more_mobile_devices) } + override val conferencesRes: Pair? + get() = context?.run { getString(R.string.conferences_title) to getCompatDrawable(R.drawable.ic_more_conferences) } + override val schoolAndTeachersRes: Pair? get() = context?.run { getString(R.string.schoolandteachers_title) to getCompatDrawable((R.drawable.ic_more_schoolandteachers)) } @@ -108,6 +112,10 @@ class MoreFragment : BaseFragment(R.layout.fragment_more), (activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance()) } + override fun openConferencesView() { + (activity as? MainActivity)?.pushView(ConferenceFragment.newInstance()) + } + override fun openSchoolAndTeachersView() { (activity as? MainActivity)?.pushView(SchoolAndTeachersFragment.newInstance()) } 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 6d80f4181..d119000d3 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,6 +1,6 @@ package io.github.wulkanowy.ui.modules.more -import io.github.wulkanowy.data.repositories.student.StudentRepository +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 @@ -27,6 +27,7 @@ class MorePresenter @Inject constructor( noteRes?.first -> openNoteView() luckyNumberRes?.first -> openLuckyNumberView() mobileDevicesRes?.first -> openMobileDevicesView() + conferencesRes?.first -> openConferencesView() schoolAndTeachersRes?.first -> openSchoolAndTeachersView() settingsRes?.first -> openSettingsView() aboutRes?.first -> openAboutView() @@ -48,6 +49,7 @@ class MorePresenter @Inject constructor( noteRes, luckyNumberRes, mobileDevicesRes, + conferencesRes, schoolAndTeachersRes, settingsRes, aboutRes 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 922afdfd5..bb1faeda4 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 @@ -15,6 +15,8 @@ interface MoreView : BaseView { val mobileDevicesRes: Pair? + val conferencesRes: Pair? + val schoolAndTeachersRes: Pair? val settingsRes: Pair? @@ -41,5 +43,7 @@ interface MoreView : BaseView { fun openMobileDevicesView() + fun openConferencesView() + fun openSchoolAndTeachersView() } 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 079285a80..403782653 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 @@ -105,8 +105,8 @@ class NoteFragment : BaseFragment(R.layout.fragment_note), binding.noteRecycler.visibility = if (show) VISIBLE else GONE } - override fun hideRefresh() { - binding.noteSwipe.isRefreshing = false + override fun showRefresh(show: Boolean) { + binding.noteSwipe.isRefreshing = show } override fun 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 db3b495a8..e80f54946 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 @@ -2,12 +2,12 @@ package io.github.wulkanowy.ui.modules.note import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Note -import io.github.wulkanowy.data.repositories.note.NoteRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.NoteRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResourceIn @@ -21,7 +21,7 @@ class NotePresenter @Inject constructor( studentRepository: StudentRepository, private val noteRepository: NoteRepository, private val semesterRepository: SemesterRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private lateinit var lastError: Throwable @@ -52,13 +52,25 @@ class NotePresenter @Inject constructor( } private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading note data started") + flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) noteRepository.getNotes(student, semester, forceRefresh) }.onEach { when (it.status) { - Status.LOADING -> Timber.i("Loading note data started") + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data) + } + } + } Status.SUCCESS -> { Timber.i("Loading note result: Success") view?.apply { @@ -80,7 +92,7 @@ class NotePresenter @Inject constructor( } }.afterLoading { view?.run { - hideRefresh() + showRefresh(false) showProgress(false) enableSwipe(true) } 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 a7cbab8f4..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 @@ -27,7 +27,7 @@ interface NoteView : BaseView { fun showContent(show: Boolean) - fun hideRefresh() + fun showRefresh(show: Boolean) fun showNoteDialog(note: Note) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersPresenter.kt index 305f4e7d5..915cc4213 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/SchoolAndTeachersPresenter.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.ui.modules.schoolandteachers -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import kotlinx.coroutines.delay diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt index 4cda5d33c..491a19ec7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt @@ -1,12 +1,12 @@ package io.github.wulkanowy.ui.modules.schoolandteachers.school import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.school.SchoolRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.SchoolRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -18,7 +18,7 @@ class SchoolPresenter @Inject constructor( studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, private val schoolRepository: SchoolRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private var address: String? = null 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 ff02116b9..c83cfe766 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt @@ -1,12 +1,12 @@ package io.github.wulkanowy.ui.modules.schoolandteachers.teacher import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.teacher.TeacherRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.TeacherRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import kotlinx.coroutines.flow.onEach @@ -18,7 +18,7 @@ class TeacherPresenter @Inject constructor( studentRepository: StudentRepository, private val semesterRepository: SemesterRepository, private val teacherRepository: TeacherRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { private lateinit var lastError: Throwable diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt index 6f4a695d6..bda07e347 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.settings -import android.content.Context import android.content.SharedPreferences import android.os.Bundle import androidx.appcompat.app.AlertDialog @@ -42,10 +41,6 @@ class SettingsFragment : PreferenceFragmentCompat(), override val syncFailedString get() = getString(R.string.pref_services_message_sync_failed) - override fun onAttach(context: Context) { - super.onAttach(context) - } - override fun initView() { findPreference(getString(R.string.pref_key_services_force_sync))?.run { onPreferenceClickListener = Preference.OnPreferenceClickListener { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt index 3d1e063e2..f9ad74a10 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsPresenter.kt @@ -2,14 +2,14 @@ package io.github.wulkanowy.ui.modules.settings import androidx.work.WorkInfo import com.chuckerteam.chucker.api.ChuckerCollector -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.AppInfo -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.isHolidays import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.onEach @@ -22,7 +22,7 @@ class SettingsPresenter @Inject constructor( studentRepository: StudentRepository, private val preferencesRepository: PreferencesRepository, private val timetableNotificationHelper: TimetableNotificationSchedulerHelper, - private val analytics: FirebaseAnalyticsHelper, + private val analytics: AnalyticsHelper, private val syncManager: SyncManager, private val chuckerCollector: ChuckerCollector, private val appInfo: AppInfo @@ -46,7 +46,10 @@ class SettingsPresenter @Inject constructor( appThemeKey -> view?.recreateView() isUpcomingLessonsNotificationsEnableKey -> if (!isUpcomingLessonsNotificationsEnable) timetableNotificationHelper.cancelNotification() appLanguageKey -> view?.run { - updateLanguage(if (appLanguage == "system") appInfo.systemLanguage else appLanguage) + val newLang = if (appLanguage == "system") appInfo.systemLanguage else appLanguage + analytics.logEvent("language", "setting_changed" to newLang) + + updateLanguage(newLang) recreateView() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt index 7fc20d238..80138175d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt @@ -8,6 +8,7 @@ import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.main.MainActivity +import io.github.wulkanowy.utils.openInternetBrowser import javax.inject.Inject @AndroidEntryPoint @@ -18,7 +19,7 @@ class SplashActivity : BaseActivity(), SplashView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - presenter.onAttachView(this) + presenter.onAttachView(this, intent?.getStringExtra("external_url")) } override fun openLoginView() { @@ -31,6 +32,11 @@ class SplashActivity : BaseActivity(), SplashView finish() } + override fun openExternalUrlAndFinish(url: String) { + openInternetBrowser(url, ::showMessage) + finish() + } + override fun showError(text: String, error: Throwable) { Toast.makeText(this, text, LENGTH_LONG).show() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt index d16ca4a80..795889171 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt @@ -1,7 +1,7 @@ package io.github.wulkanowy.ui.modules.splash import io.github.wulkanowy.data.Status -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.flowWithResource @@ -14,8 +14,13 @@ class SplashPresenter @Inject constructor( studentRepository: StudentRepository ) : BasePresenter(errorHandler, studentRepository) { - override fun onAttachView(view: SplashView) { + fun onAttachView(view: SplashView, externalUrl: String?) { super.onAttachView(view) + + if (!externalUrl.isNullOrBlank()) { + return view.openExternalUrlAndFinish(externalUrl) + } + flowWithResource { studentRepository.isCurrentStudentSet() }.onEach { when (it.status) { Status.LOADING -> Timber.d("Is current user set check started") diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt index 9efd8123b..a5aa14091 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashView.kt @@ -7,4 +7,6 @@ interface SplashView : BaseView { fun openLoginView() fun openMainView() + + fun openExternalUrlAndFinish(url: String) } 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 4aa375c21..1bb9c920b 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 @@ -16,6 +16,7 @@ import io.github.wulkanowy.databinding.FragmentTimetableBinding import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.modules.timetable.additional.AdditionalLessonsFragment import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment import io.github.wulkanowy.ui.widgets.DividerItemDecoration import io.github.wulkanowy.utils.SchooldaysRangeLimiter @@ -84,8 +85,11 @@ class TimetableFragment : BaseFragment(R.layout.fragme } override fun onOptionsItemSelected(item: MenuItem): Boolean { - return if (item.itemId == R.id.timetableMenuCompletedLessons) presenter.onCompletedLessonsSwitchSelected() - else false + return when (item.itemId) { + R.id.timetableMenuAdditionalLessons -> presenter.onAdditionalLessonsSwitchSelected() + R.id.timetableMenuCompletedLessons -> presenter.onCompletedLessonsSwitchSelected() + else -> false + } } override fun updateData(data: List, showWholeClassPlanType: String, showGroupsInPlanType: Boolean, showTimetableTimers: Boolean) { @@ -109,8 +113,8 @@ class TimetableFragment : BaseFragment(R.layout.fragme binding.timetableNavDate.text = date } - override fun hideRefresh() { - binding.timetableSwipe.isRefreshing = false + override fun showRefresh(show: Boolean) { + binding.timetableSwipe.isRefreshing = show } override fun resetView() { @@ -176,6 +180,10 @@ class TimetableFragment : BaseFragment(R.layout.fragme } } + override fun openAdditionalLessonsView() { + (activity as? MainActivity)?.pushView(AdditionalLessonsFragment.newInstance()) + } + override fun openCompletedLessonsView() { (activity as? MainActivity)?.pushView(CompletedLessonsFragment.newInstance()) } 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 f924650a0..3cd15bcfd 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 @@ -3,13 +3,13 @@ package io.github.wulkanowy.ui.modules.timetable import android.annotation.SuppressLint import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.timetable.TimetableRepository +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.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday @@ -22,7 +22,6 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onEach import timber.log.Timber -import java.lang.NullPointerException import java.time.LocalDate import java.time.LocalDate.now import java.time.LocalDate.of @@ -35,7 +34,7 @@ class TimetablePresenter @Inject constructor( private val timetableRepository: TimetableRepository, private val semesterRepository: SemesterRepository, private val prefRepository: PreferencesRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper, ) : BasePresenter(errorHandler, studentRepository) { private var baseDate: LocalDate = now().nextOrSameSchoolDay @@ -50,19 +49,19 @@ class TimetablePresenter @Inject constructor( view.initView() Timber.i("Timetable was initialized") errorHandler.showErrorMessage = ::showErrorViewOnError - loadData(ofEpochDay(date ?: baseDate.toEpochDay())) + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) + loadData() if (currentDate.isHolidays) setBaseDateOnHolidays() - reloadView() } fun onPreviousDay() { - loadData(currentDate.previousSchoolDay) - reloadView() + reloadView(currentDate.previousSchoolDay) + loadData() } fun onNextDay() { - loadData(currentDate.nextSchoolDay) - reloadView() + reloadView(currentDate.nextSchoolDay) + loadData() } fun onPickDate() { @@ -70,13 +69,13 @@ class TimetablePresenter @Inject constructor( } fun onDateSet(year: Int, month: Int, day: Int) { - loadData(of(year, month, day)) - reloadView() + reloadView(of(year, month, day)) + loadData() } fun onSwipeRefresh() { Timber.i("Force refreshing the timetable") - loadData(currentDate, true) + loadData(true) } fun onRetry() { @@ -84,7 +83,7 @@ class TimetablePresenter @Inject constructor( showErrorView(false) showProgress(true) } - loadData(currentDate, true) + loadData(true) } fun onDetailsClick() { @@ -97,8 +96,8 @@ class TimetablePresenter @Inject constructor( if (view.currentStackSize == 1) { baseDate.also { if (currentDate != it) { - loadData(it) - reloadView() + reloadView(it) + loadData() } else if (!view.isViewEmpty) view.resetView() } } else view.popView() @@ -110,6 +109,11 @@ class TimetablePresenter @Inject constructor( view?.showTimetableDialog(lesson) } + fun onAdditionalLessonsSwitchSelected(): Boolean { + view?.openAdditionalLessonsView() + return true + } + fun onCompletedLessonsSwitchSelected(): Boolean { view?.openCompletedLessonsView() return true @@ -128,35 +132,38 @@ class TimetablePresenter @Inject constructor( }.launch("holidays") } - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - currentDate = date + private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading timetable data started") flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) - timetableRepository.getTimetable(student, semester, date, date, forceRefresh) + timetableRepository.getTimetable(student, semester, currentDate, currentDate, forceRefresh) }.onEach { when (it.status) { - Status.LOADING -> Timber.i("Loading timetable data started") + Status.LOADING -> { + if (!it.data?.first.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data!!.first) + } + } + } Status.SUCCESS -> { Timber.i("Loading timetable result: Success") view?.apply { - updateData( - showWholeClassPlanType = prefRepository.showWholeClassPlan, - showGroupsInPlanType = prefRepository.showGroupsInPlan, - showTimetableTimers = prefRepository.showTimetableTimers, - data = it.data!! - .filter { item -> if (prefRepository.showWholeClassPlan == "no") item.isStudentPlan else true } - .sortedWith(compareBy({ item -> item.number }, { item -> !item.isStudentPlan })) - ) - showEmpty(it.data.isEmpty()) + updateData(it.data!!.first) + showEmpty(it.data.first.isEmpty()) showErrorView(false) - showContent(it.data.isNotEmpty()) + showContent(it.data.first.isNotEmpty()) } analytics.logEvent( "load_data", "type" to "timetable", - "items" to it.data!!.size + "items" to it.data!!.first.size ) } Status.ERROR -> { @@ -166,13 +173,26 @@ class TimetablePresenter @Inject constructor( } }.afterLoading { view?.run { - hideRefresh() + showRefresh(false) showProgress(false) enableSwipe(true) } }.launch() } + private fun updateData(lessons: List) { + view?.updateData( + showWholeClassPlanType = prefRepository.showWholeClassPlan, + showGroupsInPlanType = prefRepository.showGroupsInPlan, + showTimetableTimers = prefRepository.showTimetableTimers, + data = createItems(lessons) + ) + } + + private fun createItems(items: List) = items.filter { item -> + if (prefRepository.showWholeClassPlan == "no") item.isStudentPlan else true + }.sortedWith(compareBy({ item -> item.number }, { item -> !item.isStudentPlan })) + private fun showErrorViewOnError(message: String, error: Throwable) { view?.run { if (isViewEmpty) { @@ -184,7 +204,9 @@ class TimetablePresenter @Inject constructor( } } - private fun reloadView() { + private fun reloadView(date: LocalDate) { + currentDate = date + Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}") view?.apply { showProgress(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 244120176..c6bceb9e8 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 @@ -18,7 +18,7 @@ interface TimetableView : BaseView { fun clearData() - fun hideRefresh() + fun showRefresh(show: Boolean) fun resetView() @@ -44,5 +44,7 @@ interface TimetableView : BaseView { fun popView() + fun openAdditionalLessonsView() + fun openCompletedLessonsView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt new file mode 100644 index 000000000..fdc8b8874 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt @@ -0,0 +1,35 @@ +package io.github.wulkanowy.ui.modules.timetable.additional + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.databinding.ItemTimetableAdditionalBinding +import io.github.wulkanowy.utils.toFormattedString +import javax.inject.Inject + +class AdditionalLessonsAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var items = emptyList() + + override fun getItemCount() = items.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + ItemTimetableAdditionalBinding.inflate(LayoutInflater.from(parent.context), parent, false) + ) + + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + val item = items[position] + + with(holder.binding) { + additionalLessonItemTime.text = "${item.start.toFormattedString("HH:mm")} - ${item.end.toFormattedString("HH:mm")}" + additionalLessonItemSubject.text = item.subject + } + } + + class ItemViewHolder(val binding: ItemTimetableAdditionalBinding) : + RecyclerView.ViewHolder(binding.root) +} 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 new file mode 100644 index 000000000..17a1cabeb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt @@ -0,0 +1,144 @@ +package io.github.wulkanowy.ui.modules.timetable.additional + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import com.wdullaer.materialdatetimepicker.date.DatePickerDialog +import dagger.hilt.android.AndroidEntryPoint +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.databinding.FragmentTimetableAdditionalBinding +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.ui.widgets.DividerItemDecoration +import io.github.wulkanowy.utils.SchooldaysRangeLimiter +import io.github.wulkanowy.utils.dpToPx +import java.time.LocalDate +import javax.inject.Inject + +@AndroidEntryPoint +class AdditionalLessonsFragment : + BaseFragment(R.layout.fragment_timetable_additional), + AdditionalLessonsView, MainView.TitledView { + + @Inject + lateinit var presenter: AdditionalLessonsPresenter + + @Inject + lateinit var additionalLessonsAdapter: AdditionalLessonsAdapter + + companion object { + private const val SAVED_DATE_KEY = "CURRENT_DATE" + + fun newInstance() = AdditionalLessonsFragment() + } + + override val titleStringId get() = R.string.additional_lessons_title + + override val isViewEmpty get() = additionalLessonsAdapter.items.isEmpty() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentTimetableAdditionalBinding.bind(view) + messageContainer = binding.additionalLessonsRecycler + presenter.onAttachView(this, savedInstanceState?.getLong(SAVED_DATE_KEY)) + } + + override fun initView() { + with(binding.additionalLessonsRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = additionalLessonsAdapter + addItemDecoration(DividerItemDecoration(context)) + } + + with(binding) { + additionalLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) + additionalLessonsErrorRetry.setOnClickListener { presenter.onRetry() } + + additionalLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() } + additionalLessonsNavDate.setOnClickListener { presenter.onPickDate() } + additionalLessonsNextButton.setOnClickListener { presenter.onNextDay() } + + additionalLessonsNavContainer.setElevationCompat(requireContext().dpToPx(8f)) + } + } + + override fun updateData(data: List) { + with(additionalLessonsAdapter) { + items = data + notifyDataSetChanged() + } + } + + override fun clearData() { + with(additionalLessonsAdapter) { + items = emptyList() + notifyDataSetChanged() + } + } + + override fun updateNavigationDay(date: String) { + binding.additionalLessonsNavDate.text = date + } + + override fun hideRefresh() { + binding.additionalLessonsSwipe.isRefreshing = false + } + + override fun showEmpty(show: Boolean) { + binding.additionalLessonsEmpty.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showErrorView(show: Boolean) { + binding.additionalLessonsError.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun setErrorDetails(message: String) { + binding.additionalLessonsErrorMessage.text = message + } + + override fun showProgress(show: Boolean) { + binding.additionalLessonsProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun enableSwipe(enable: Boolean) { + binding.additionalLessonsSwipe.isEnabled = enable + } + + override fun showContent(show: Boolean) { + binding.additionalLessonsRecycler.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showPreButton(show: Boolean) { + binding.additionalLessonsPreviousButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showNextButton(show: Boolean) { + binding.additionalLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE + } + + override fun showDatePickerDialog(currentDate: LocalDate) { + val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth -> + presenter.onDateSet(year, month + 1, dayOfMonth) + } + val datePickerDialog = DatePickerDialog.newInstance(dateSetListener, + currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth) + + with(datePickerDialog) { + setDateRangeLimiter(SchooldaysRangeLimiter()) + version = DatePickerDialog.Version.VERSION_2 + scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL + show(this@AdditionalLessonsFragment.parentFragmentManager, null) + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt new file mode 100644 index 000000000..623160e7e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt @@ -0,0 +1,166 @@ +package io.github.wulkanowy.ui.modules.timetable.additional + +import android.annotation.SuppressLint +import io.github.wulkanowy.data.Status +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.TimetableRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AnalyticsHelper +import io.github.wulkanowy.utils.afterLoading +import io.github.wulkanowy.utils.flowWithResourceIn +import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday +import io.github.wulkanowy.utils.isHolidays +import io.github.wulkanowy.utils.nextOrSameSchoolDay +import io.github.wulkanowy.utils.nextSchoolDay +import io.github.wulkanowy.utils.previousSchoolDay +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import java.time.LocalDate +import javax.inject.Inject + +class AdditionalLessonsPresenter @Inject constructor( + studentRepository: StudentRepository, + errorHandler: ErrorHandler, + private val semesterRepository: SemesterRepository, + private val timetableRepository: TimetableRepository, + private val analytics: AnalyticsHelper +) : BasePresenter(errorHandler, studentRepository) { + + private var baseDate: LocalDate = LocalDate.now().nextOrSameSchoolDay + + lateinit var currentDate: LocalDate + private set + + private lateinit var lastError: Throwable + + fun onAttachView(view: AdditionalLessonsView, date: Long?) { + super.onAttachView(view) + view.initView() + Timber.i("Additional lessons was initialized") + errorHandler.showErrorMessage = ::showErrorViewOnError + loadData(LocalDate.ofEpochDay(date ?: baseDate.toEpochDay())) + if (currentDate.isHolidays) setBaseDateOnHolidays() + reloadView() + } + + fun onPreviousDay() { + loadData(currentDate.previousSchoolDay) + reloadView() + } + + fun onNextDay() { + loadData(currentDate.nextSchoolDay) + reloadView() + } + + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onDateSet(year: Int, month: Int, day: Int) { + loadData(LocalDate.of(year, month, day)) + reloadView() + } + + fun onSwipeRefresh() { + Timber.i("Force refreshing the additional lessons") + loadData(currentDate, true) + } + + fun onRetry() { + view?.run { + showErrorView(false) + showProgress(true) + } + loadData(currentDate, 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(date: LocalDate, forceRefresh: Boolean = false) { + currentDate = date + + flowWithResourceIn { + val student = studentRepository.getCurrentStudent() + val semester = semesterRepository.getCurrentSemester(student) + timetableRepository.getTimetable(student, semester, date, date, forceRefresh, true) + }.onEach { + when (it.status) { + Status.LOADING -> Timber.i("Loading additional lessons data started") + Status.SUCCESS -> { + Timber.i("Loading additional lessons lessons result: Success") + view?.apply { + updateData(it.data!!.second.sortedBy { item -> item.date }) + showEmpty(it.data.second.isEmpty()) + showErrorView(false) + showContent(it.data.second.isNotEmpty()) + } + analytics.logEvent( + "load_data", + "type" to "additional_lessons", + "items" to it.data!!.second.size + ) + } + Status.ERROR -> { + Timber.i("Loading additional lessons result: An exception occurred") + errorHandler.dispatch(it.error!!) + } + } + }.afterLoading { + view?.run { + hideRefresh() + showProgress(false) + enableSwipe(true) + } + }.launch() + } + + private fun showErrorViewOnError(message: String, error: Throwable) { + view?.run { + if (isViewEmpty) { + lastError = error + setErrorDetails(message) + showErrorView(true) + showEmpty(false) + } else showError(message, error) + } + } + + private fun reloadView() { + Timber.i("Reload additional lessons view with the date ${currentDate.toFormattedString()}") + view?.apply { + showProgress(true) + enableSwipe(false) + showContent(false) + showEmpty(false) + showErrorView(false) + clearData() + reloadNavigation() + } + } + + @SuppressLint("DefaultLocale") + private fun reloadNavigation() { + view?.apply { + showPreButton(!currentDate.minusDays(1).isHolidays) + showNextButton(!currentDate.plusDays(1).isHolidays) + updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalize()) + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt new file mode 100644 index 000000000..97eb2ae7b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt @@ -0,0 +1,38 @@ +package io.github.wulkanowy.ui.modules.timetable.additional + +import io.github.wulkanowy.data.db.entities.TimetableAdditional +import io.github.wulkanowy.ui.base.BaseView +import java.time.LocalDate + +interface AdditionalLessonsView : BaseView { + + val isViewEmpty: Boolean + + fun initView() + + fun updateData(data: List) + + fun clearData() + + fun updateNavigationDay(date: String) + + fun hideRefresh() + + fun showEmpty(show: Boolean) + + fun showErrorView(show: Boolean) + + fun setErrorDetails(message: String) + + fun showProgress(show: Boolean) + + fun enableSwipe(enable: Boolean) + + fun showContent(show: Boolean) + + fun showPreButton(show: Boolean) + + fun showNextButton(show: Boolean) + + fun showDatePickerDialog(currentDate: LocalDate) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt index 767c2f313..00ba0bad8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt @@ -1,15 +1,11 @@ package io.github.wulkanowy.ui.modules.timetable.completed import android.content.res.Resources -import com.chuckerteam.chucker.api.ChuckerCollector import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject -class CompletedLessonsErrorHandler @Inject constructor( - resources: Resources, - chuckerCollector: ChuckerCollector -) : ErrorHandler(resources, chuckerCollector) { +class CompletedLessonsErrorHandler @Inject constructor(resources: Resources) : ErrorHandler(resources) { var onFeatureDisabled: () -> Unit = {} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt index 5b77fc1f3..828e10014 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 @@ -89,8 +89,8 @@ class CompletedLessonsFragment : binding.completedLessonsNavDate.text = date } - override fun hideRefresh() { - binding.completedLessonsSwipe.isRefreshing = false + override fun showRefresh(show: Boolean) { + binding.completedLessonsSwipe.isRefreshing = show } override fun showEmpty(show: Boolean) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt index f1c2cedc6..04e2a5cd7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt @@ -3,11 +3,11 @@ package io.github.wulkanowy.ui.modules.timetable.completed import android.annotation.SuppressLint import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.entities.CompletedLesson -import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.CompletedLessonsRepository +import io.github.wulkanowy.data.repositories.SemesterRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday @@ -30,7 +30,7 @@ class CompletedLessonsPresenter @Inject constructor( private val completedLessonsErrorHandler: CompletedLessonsErrorHandler, private val semesterRepository: SemesterRepository, private val completedLessonsRepository: CompletedLessonsRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: AnalyticsHelper ) : BasePresenter(completedLessonsErrorHandler, studentRepository) { private var baseDate: LocalDate = now().nextOrSameSchoolDay @@ -50,19 +50,19 @@ class CompletedLessonsPresenter @Inject constructor( this.view?.showEmpty(true) Timber.i("Completed lessons feature disabled by school") } - loadData(ofEpochDay(date ?: baseDate.toEpochDay())) + reloadView(ofEpochDay(date ?: baseDate.toEpochDay())) + loadData() if (currentDate.isHolidays) setBaseDateOnHolidays() - reloadView() } fun onPreviousDay() { - loadData(currentDate.previousSchoolDay) - reloadView() + reloadView(currentDate.previousSchoolDay) + loadData() } fun onNextDay() { - loadData(currentDate.nextSchoolDay) - reloadView() + reloadView(currentDate.nextSchoolDay) + loadData() } fun onPickDate() { @@ -70,13 +70,13 @@ class CompletedLessonsPresenter @Inject constructor( } fun onDateSet(year: Int, month: Int, day: Int) { - loadData(LocalDate.of(year, month, day)) - reloadView() + reloadView(LocalDate.of(year, month, day)) + loadData() } fun onSwipeRefresh() { Timber.i("Force refreshing the completed lessons") - loadData(currentDate, true) + loadData(true) } fun onRetry() { @@ -84,7 +84,7 @@ class CompletedLessonsPresenter @Inject constructor( showErrorView(false) showProgress(true) } - loadData(currentDate, true) + loadData(true) } fun onDetailsClick() { @@ -109,16 +109,26 @@ class CompletedLessonsPresenter @Inject constructor( }.launch("holidays") } - private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { - currentDate = date + private fun loadData(forceRefresh: Boolean = false) { + Timber.i("Loading completed lessons data started") flowWithResourceIn { val student = studentRepository.getCurrentStudent() val semester = semesterRepository.getCurrentSemester(student) - completedLessonsRepository.getCompletedLessons(student, semester, date, date, forceRefresh) + completedLessonsRepository.getCompletedLessons(student, semester, currentDate, currentDate, forceRefresh) }.onEach { when (it.status) { - Status.LOADING -> Timber.i("Loading completed lessons data started") + Status.LOADING -> { + if (!it.data.isNullOrEmpty()) { + view?.run { + enableSwipe(true) + showRefresh(true) + showProgress(false) + showContent(true) + updateData(it.data.sortedBy { item -> item.number }) + } + } + } Status.SUCCESS -> { Timber.i("Loading completed lessons lessons result: Success") view?.apply { @@ -140,7 +150,7 @@ class CompletedLessonsPresenter @Inject constructor( } }.afterLoading { view?.run { - hideRefresh() + showRefresh(false) showProgress(false) enableSwipe(true) } @@ -158,7 +168,9 @@ class CompletedLessonsPresenter @Inject constructor( } } - private fun reloadView() { + private fun reloadView(date: LocalDate) { + currentDate = date + Timber.i("Reload completed lessons view with the date ${currentDate.toFormattedString()}") view?.apply { showProgress(true) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt index 7e92cc63c..7a98874e0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt @@ -16,7 +16,7 @@ interface CompletedLessonsView : BaseView { fun updateNavigationDay(date: String) - fun hideRefresh() + fun showRefresh(show: Boolean) fun showEmpty(show: Boolean) 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 2274ecb90..67805fe0b 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 @@ -3,7 +3,7 @@ package io.github.wulkanowy.ui.modules.timetablewidget import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.repositories.student.StudentRepository +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.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey 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 c7b4d4a84..df656c008 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 @@ -14,10 +14,10 @@ import android.widget.RemoteViewsService import io.github.wulkanowy.R import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import io.github.wulkanowy.data.repositories.semester.SemesterRepository -import io.github.wulkanowy.data.repositories.student.StudentRepository -import io.github.wulkanowy.data.repositories.timetable.TimetableRepository +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.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 @@ -107,7 +107,7 @@ class TimetableWidgetFactory( val semester = semesterRepository.getCurrentSemester(student) timetableRepository.getTimetable(student, semester, date, date, false) - .toFirstResult().data.orEmpty() + .toFirstResult().data?.first.orEmpty() .sortedWith(compareBy({ it.number }, { !it.isStudentPlan })) .filter { if (prefRepository.showWholeClassPlan == "no") it.isStudentPlan else true } } 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 60ad9d5cc..938be98da 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 @@ -19,12 +19,12 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.exceptions.NoCurrentStudentException -import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.data.repositories.StudentRepository import io.github.wulkanowy.services.HiltBroadcastReceiver import io.github.wulkanowy.services.widgets.TimetableWidgetService import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.AnalyticsHelper import io.github.wulkanowy.utils.nextOrSameSchoolDay import io.github.wulkanowy.utils.nextSchoolDay import io.github.wulkanowy.utils.previousSchoolDay @@ -49,7 +49,7 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() { lateinit var sharedPref: SharedPrefProvider @Inject - lateinit var analytics: FirebaseAnalyticsHelper + lateinit var analytics: AnalyticsHelper companion object { diff --git a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt index 724458c04..2d074b222 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt @@ -18,7 +18,7 @@ inline fun networkBoundResource( crossinline query: () -> Flow, crossinline fetch: suspend (ResultType) -> RequestType, crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit, - crossinline onFetchFailed: (Throwable) -> Unit = { Unit }, + crossinline onFetchFailed: (Throwable) -> Unit = { }, crossinline shouldFetch: (ResultType) -> Boolean = { true }, crossinline filterResult: (ResultType) -> ResultType = { it } ) = flow { @@ -29,7 +29,8 @@ inline fun networkBoundResource( if (showSavedOnLoading) emit(Resource.loading(filterResult(data))) try { - saveFetchResult(data, fetch(data)) + val newData = fetch(data) + saveFetchResult(data, newData) query().map { Resource.success(filterResult(it)) } } catch (throwable: Throwable) { onFetchFailed(throwable) @@ -83,7 +84,7 @@ fun flowWithResourceIn(block: suspend () -> Flow>) = flow { block() .catch { emit(Resource.error(it)) } .collect { - if (it.status != Status.LOADING) { // LOADING is already emitted + if (it.status != Status.LOADING || (it.status == Status.LOADING && it.data != null)) { // LOADING without data is already emitted emit(it) } } 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 fc3528495..48d46892b 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt @@ -22,32 +22,32 @@ private fun Bundle?.checkSavedState() = if (this == null) "(STATE IS NULL)" else class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks { - override fun onActivityPaused(activity: Activity?) { - activity?.let { Timber.d("${it::class.java.simpleName} PAUSED") } + override fun onActivityPaused(activity: Activity) { + Timber.d("${activity::class.java.simpleName} PAUSED") } - override fun onActivityResumed(activity: Activity?) { - activity?.let { Timber.d("${it::class.java.simpleName} RESUMED") } + override fun onActivityResumed(activity: Activity) { + Timber.d("${activity::class.java.simpleName} RESUMED") } - override fun onActivityStarted(activity: Activity?) { - activity?.let { Timber.d("${it::class.java.simpleName} STARTED") } + override fun onActivityStarted(activity: Activity) { + Timber.d("${activity::class.java.simpleName} STARTED") } - override fun onActivityDestroyed(activity: Activity?) { - activity?.let { Timber.d("${it::class.java.simpleName} DESTROYED") } + override fun onActivityDestroyed(activity: Activity) { + Timber.d("${activity::class.java.simpleName} DESTROYED") } - override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) { - activity?.let { Timber.d("${it::class.java.simpleName} SAVED INSTANCE STATE") } + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + Timber.d("${activity::class.java.simpleName} SAVED INSTANCE STATE") } - override fun onActivityStopped(activity: Activity?) { - activity?.let { Timber.d("${it::class.java.simpleName} STOPPED") } + override fun onActivityStopped(activity: Activity) { + Timber.d("${activity::class.java.simpleName} STOPPED") } - override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { - activity?.let { Timber.d("${it::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}") } + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + Timber.d("${activity::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}") } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt new file mode 100644 index 000000000..cd59b8648 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.utils + +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.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.enums.MessageFolder +import timber.log.Timber +import java.time.LocalDate +import java.time.LocalDateTime +import javax.inject.Inject + +fun getRefreshKey(name: String, semester: Semester, start: LocalDate, end: LocalDate): String { + return "${name}_${semester.studentId}_${semester.semesterId}_${start.monday}_${end.sunday}" +} + +fun getRefreshKey(name: String, semester: Semester): String { + return "${name}_${semester.studentId}_${semester.semesterId}" +} + +fun getRefreshKey(name: String, student: Student): String { + return "${name}_${student.userLoginId}" +} + +fun getRefreshKey(name: String, student: Student, folder: MessageFolder): String { + return "${name}_${student.id}_${folder.id}" +} + +class AutoRefreshHelper @Inject constructor( + @ApplicationContext private val context: Context, + private val sharedPref: SharedPrefProvider +) { + + fun isShouldBeRefreshed(key: String): Boolean { + val timestamp = sharedPref.getLong(key, 0).toLocalDateTime() + val servicesInterval = sharedPref.getString(context.getString(R.string.pref_key_services_interval), context.getString(R.string.pref_default_services_interval)).toLong() + + val shouldBeRefreshed = timestamp < LocalDateTime.now().minusMinutes(servicesInterval) + + Timber.d("Check if $key need to be refreshed: $shouldBeRefreshed (last refresh: $timestamp, interval: $servicesInterval min)") + + return shouldBeRefreshed + } + + fun updateLastRefreshTimestamp(key: String) { + sharedPref.putLong(key, LocalDateTime.now().toTimestamp()) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt index de9f656a2..da5fd3dbb 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt @@ -4,20 +4,26 @@ import android.content.res.Resources import io.github.wulkanowy.R import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException +import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException +import io.github.wulkanowy.sdk.scrapper.exception.VulcanException import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException +import okhttp3.internal.http2.StreamResetException import java.io.InterruptedIOException +import java.net.ConnectException import java.net.SocketTimeoutException import java.net.UnknownHostException fun Resources.getString(error: Throwable) = when (error) { is UnknownHostException -> getString(R.string.error_no_internet) - is SocketTimeoutException, is InterruptedIOException -> getString(R.string.error_timeout) + is SocketTimeoutException, is InterruptedIOException, is ConnectException, is StreamResetException -> getString(R.string.error_timeout) is NotLoggedInException -> getString(R.string.error_login_failed) is PasswordChangeRequiredException -> getString(R.string.error_password_change_required) is ServiceUnavailableException -> getString(R.string.error_service_unavailable) is FeatureDisabledException -> getString(R.string.error_feature_disabled) is FeatureNotAvailableException -> getString(R.string.error_feature_not_available) + is VulcanException -> getString(R.string.error_unknown_uonet) + is ScrapperException -> getString(R.string.error_unknown_app) else -> getString(R.string.error_unknown) } diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 7553e2cb7..b13ea2639 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,10 +1,9 @@ -Wersja 0.21.2 -- naprawiliśmy logowanie do tarnowskiego dziennika -- naprawiliśmy wyświetlanie podsumowania punktów klasy -- dodaliśmy skróty aplikacji -- dodaliśmy opcję pokazywania nazwy grupy w planie lekcji na liście -- dodaliśmy opcję pokazywania w ocenach przedmiotów bez ocen -- dodaliśmy dodatkowe opcje sortowania ocen -- dodaliśmy rozróżnianie powiadomień dla pochwał i uwag +Wersja 0.24.1 +- naprawiliśmy odczytywanie wiadomości +- naprawiliśmy niekończące się ładowanie w ocenach na drugim semestrze +- naprawiliśmy ciemny motyw na MIUI 12 +- dodaliśmy automatyczne odświeżanie danych w aplikacji +- naprawiliśmy wysyłanie wiadomości kiedy uczeń zalogowany był/jest przez konto ucznia i rodzica +- dodaliśmy zakładkę lekcji dodatkowych (na górnym pasku w planie lekcji) Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases diff --git a/app/src/main/res/drawable/ic_about_facebook.xml b/app/src/main/res/drawable/ic_about_facebook.xml new file mode 100644 index 000000000..a1b7b46eb --- /dev/null +++ b/app/src/main/res/drawable/ic_about_facebook.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_timetable_lessons_additional.xml b/app/src/main/res/drawable/ic_menu_timetable_lessons_additional.xml new file mode 100644 index 000000000..169395e91 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_timetable_lessons_additional.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_more_conferences.xml b/app/src/main/res/drawable/ic_more_conferences.xml new file mode 100644 index 000000000..87be8e055 --- /dev/null +++ b/app/src/main/res/drawable/ic_more_conferences.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 2ea0a4d39..2f88ecc82 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - + app:contentInsetStartWithNavigation="0dp" + app:layout_constraintTop_toTopOf="parent" /> + android:layout_height="0dp" + app:layout_constraintBottom_toTopOf="@id/mainBottomNav" + app:layout_constraintTop_toBottomOf="@id/mainToolbar" /> - + app:layout_constraintBottom_toBottomOf="parent" /> + diff --git a/app/src/main/res/layout/fragment_conference.xml b/app/src/main/res/layout/fragment_conference.xml new file mode 100644 index 000000000..d7cac8786 --- /dev/null +++ b/app/src/main/res/layout/fragment_conference.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_timetable_additional.xml b/app/src/main/res/layout/fragment_timetable_additional.xml new file mode 100644 index 000000000..d70306c9d --- /dev/null +++ b/app/src/main/res/layout/fragment_timetable_additional.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_about.xml b/app/src/main/res/layout/item_about.xml index f988c47ba..6a95e5529 100644 --- a/app/src/main/res/layout/item_about.xml +++ b/app/src/main/res/layout/item_about.xml @@ -33,7 +33,8 @@ + android:orientation="vertical" + app:layout_constraintBottom_toBottomOf="@+id/attendanceItemDetailsContainer" + app:layout_constraintEnd_toStartOf="@+id/attendanceItemDetailsContainer" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@+id/attendanceItemDetailsContainer"> - + android:layout_marginEnd="10dp" + app:layout_constraintEnd_toStartOf="@+id/attendanceItemAlert" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toEndOf="@+id/attendanceItemNumberContainer" + app:layout_constraintTop_toTopOf="parent"> - + + + + - + diff --git a/app/src/main/res/layout/item_attendance_summary_subject.xml b/app/src/main/res/layout/item_attendance_summary_subject.xml index 263c08a73..ff731461d 100644 --- a/app/src/main/res/layout/item_attendance_summary_subject.xml +++ b/app/src/main/res/layout/item_attendance_summary_subject.xml @@ -3,7 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/attendanceSummaryItemSubject" android:layout_width="match_parent" - android:layout_height="48dp" + android:layout_height="wrap_content" + android:minHeight="48dp" android:gravity="start" android:maxLines="1" android:paddingLeft="16dp" diff --git a/app/src/main/res/layout/item_completed_lesson.xml b/app/src/main/res/layout/item_completed_lesson.xml index b9beec804..e9944f56f 100644 --- a/app/src/main/res/layout/item_completed_lesson.xml +++ b/app/src/main/res/layout/item_completed_lesson.xml @@ -12,8 +12,11 @@ diff --git a/app/src/main/res/layout/item_conference.xml b/app/src/main/res/layout/item_conference.xml new file mode 100644 index 000000000..4c881f344 --- /dev/null +++ b/app/src/main/res/layout/item_conference.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_grade_details.xml b/app/src/main/res/layout/item_grade_details.xml index ccc968c08..2f3bd2de5 100644 --- a/app/src/main/res/layout/item_grade_details.xml +++ b/app/src/main/res/layout/item_grade_details.xml @@ -18,62 +18,82 @@ - + app:layout_constraintTop_toTopOf="parent"> - + - + + + + + diff --git a/app/src/main/res/layout/item_license.xml b/app/src/main/res/layout/item_license.xml index aaa7cd349..cb55ab87a 100644 --- a/app/src/main/res/layout/item_license.xml +++ b/app/src/main/res/layout/item_license.xml @@ -2,7 +2,8 @@ + + + + + + diff --git a/app/src/main/res/layout/item_timetable_small.xml b/app/src/main/res/layout/item_timetable_small.xml index 98a213ec3..037c10d5d 100644 --- a/app/src/main/res/layout/item_timetable_small.xml +++ b/app/src/main/res/layout/item_timetable_small.xml @@ -7,7 +7,8 @@ diff --git a/app/src/main/res/menu/action_menu_timetable.xml b/app/src/main/res/menu/action_menu_timetable.xml index c3d0222c1..47257f5b4 100644 --- a/app/src/main/res/menu/action_menu_timetable.xml +++ b/app/src/main/res/menu/action_menu_timetable.xml @@ -1,6 +1,13 @@ + + + + Světlý + Tmavý + Černý (AMOLED) + + + Jazyk systému + Polski + English + Pусский + Українська + Deutsch + Čeština + + + 15 minut + 30 minut + 1 hodina + 2 hodiny + 6 hodin + 12 hodin + 24 hodin + + + 0,00 + 0,25 + 0,33 + 0,5 + 0,75 + + + Abecedně + Podle data + + + Dzienniczek+ + Wulkanowy + Barvy známek v deníku + + + Průměrná známka od druhého semestru + Průměr známek z obou semestrů + Průměr známek z celého roku + + + Neukaž + Ukázat vše + Ukázat menší + + diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml new file mode 100644 index 000000000..e2e7bdac0 --- /dev/null +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -0,0 +1,474 @@ + + + + Přihlášení + Wulkanowy + Známky + Docházka + Zkoušky + Plán lekce + Nastavení + Více + O aplikaci + Prohlížeč protokolů + Tvůrci + Licence + Zprávy + Nová zpráva + Poznámky a úspěchy + Domácí úkoly + Vyberte účet + + Semestr %1$d, %2$d/%3$d + + Přihlaste se pomocí studentského nebo rodičovského účtu + Zadejte symbol ze stránky deníku + Uživatelské jméno + Email + Přihlášení, číslo PESEL nebo e-mail + Heslo + Variace deníku UONET+ + Mobile API + Scraper + Hybridní + Token + PIN + Klíč API + Symbol + Přihlásit + Toto heslo je příliš krátké + Přihlašovací údaje jsou nesprávné. Ujistěte se, že je v poli níže vybrána správná variace deníku UONET+ + Neplatný PIN + Neplatný token + Platnost tokenu vypršela + Neplatný e-mail + Místo e-mailu použijte přiřazené přihlašovací údaje + Neplatný symbol + Student nebyl nalezen. Zkontrolujte správnost symbolu a vybrané varianty deníku UONET+ + Toto pole je povinné + Vybraný student je již přihlášen + Symbol najdete na stránce deníku v  Uczeń →  Dostęp Mobilny →  Zarejestruj urządzenie mobilne.\n\nUjistěte se, že jste na předchozí obrazovce nastavili správnou variantu deníku do pole Variace deníku UONET+. Wulkanowy v tuto chvíli nezjistí předškolní studenty + Vyberte studenty, kteří se mají do aplikace přihlásit + Jiné možnosti + V tomto režimu nefungují následující: šťastné číslo, statistiky třídy, shrnutí docházky, ospravedlnění nepřítomnosti, absolvované lekce, informace o škole a prohlížení seznamu registrovaných zařízení + Tento režim zobrazuje stejná data, která se zobrazují na webových stránkách deníka + Kombinace nejlepších vlastností ostatních dvou režimů. Funguje rychleji než scraper a poskytuje funkce, které nejsou k dispozici v režimu Mobile API. Je to v experimentální fázi + Zásady ochrany osobních údajů + Problémy s přihlášením? Napište nám! + Email + Discord + Poslat e-mail + Popište podrobnosti problému: + Ujistěte se, že jste vybrali správnou variantu deníku UONET+! + Zapomněl jsem své heslo + Obnovte svůj účet + Obnovit + Student je již přihlášen + + Manažer účtů + Přihlásit se + Platnost relace vypršela + Vaše relace vypršela, přihlaste se prosím znovu + + Známka + Semestr %d + Změnit semestr + Žádné známky + Váha + Váha: %s + Komentář + Žádné nové známky + Počet nových známek: %1$d + Průměr: %1$.2f + Body: %s + Bez průměru + Předpovězeno: %1$s + Konečná: %1$s + Celkem bodů + Konečná známka + Předpokládaná známka + Vypočítaný průměr + Konečný průměr + Souhrn + Třída + Označit jako přečtené + Částečně + Semestr + Body + + %d známka + %d známky + %d známek + %d známek + + + Nová známka + Nové známky + Nové známky + Nové známky + + + Nová předpokládaná známka + Nové předpovídané známky + Nové předpovídané známky + Nové předpovídané známky + + + Nová konečná známka + Nové konečné známky + Nové konečné známky + Nové konečné známky + + + Máte %1$d novou známku + Máte %1$d nové známky + Máte %1$d nové známky + Máte %1$d nové známky + + + Máte %1$d novou předpokládanou známku + Máte %1$d nové předpokládané známky + Máte %1$d nové předpokládané známky + Máte %1$d nové předpokládané známky + + + Máte %1$d novou konečnou známku + Máte %1$d nové konečné známky + Máte %1$d nové konečné známky + Máte %1$d nové konečné známky + + + Lekce + Pokoj + Skupina + Hodiny + Změny + Žádné lekce tento den + %s min + %s sek + dosud %1$s + za %1$s + Lekce skončila + Nyní: %s + Okamžik: %s + Později: %s + + Dokončené lekce + Zobrazit dokončené lekce + Žádné informace o dokončených lekcích + Téma + Nepřítomnost + Zdroje + + Další lekce + Zobrazit další lekce + Žádné informace o dalších lekcích + + Souhrn docházky + Nepřítomen ze školních důvodů + Omluvená nepřítomnost + Neomluvená absence + Osvobození + Omluvené zpoždění + Neomluvené zpoždění + Přítomnost + Smazáno + Neznámý + Číslo lekce + Žádné položky + + %1$d nepřítomnost + %1$d nepřítomnosti + %1$d nepřítomnosti + %1$d nepřítomnosti + + Důvod nepřítomnosti (volitelný) + Poslat + Nepřítomnost úspěšně omluvena! + Musíte vybrat alespoň jednu nepřítomnost! + Ospravedlnit + + Účast + Celkový + + Tento týden žádné testy + Typ + Datum vstupu + + Doručená pošta + Odesláno + Koš + (žádné téma) + Žádné zprávy + Při stahování obsahu zprávy došlo k chybě + Od: + Komu: + Datum: %s + Odpověď + Poslat dále + Vymazat + Přesunout do koše + Trvale smazat + Zpráva byla úspěšně smazána + Podíl + Vytisknout + Téma + Obsah + Zpráva úspěšně odeslána + Message does not exist + Musíte vybrat alespoň 1 příjemce + Obsah zprávy musí mít alespoň 3 znaky + + %d zpráva + %d zprávy + %d zprávy + %d zprávy + + + Nová zpráva + Nové zprávy + Nové zprávy + Nové zprávy + + + Máte %1$d novou zprávu + Máte %1$d nové zprávy + Máte %1$d nové zprávy + Máte %1$d nové zprávy + + + Žádné informace o poznámkách + Body + + %d poznámka + %d poznámky + %d poznámky + %d poznámky + + + Nová poznámka + Nové poznámky + Nové poznámky + Nové poznámky + + + Máte %1$d novou poznámku + Máte %1$d nové poznámky + Máte %1$d nové poznámky + Máte %1$d nové poznámky + + + + %d chvála + %d chvály + %d chvály + %d chvály + + + Nová chvála + Nové chvály + Nové chvály + Nové chvály + + + Máte %1$d novou chválu + Máte %1$d nové chvály + Máte %1$d nové chvály + Máte %1$d nové chvály + + + + %d neutrální poznámka + %d neutrální poznámky + %d neutrální poznámky + %d neutrální poznámky + + + Nová neutrální poznámka + Nové neutrální poznámky + Nové neutrální poznámky + Nové neutrální poznámky + + + Máte %1$d novou neutrální pozornost + Máte %1$d nové neutrální pozornosti + Máte %1$d nové neutrální pozornosti + Máte %1$d nové neutrální pozornosti + + + Žádné informace o domácích úkolech + Označit jako hotové + Neudělané + Přílohy + + Šťastné číslo + Dnešní šťastné číslo je + Žádné informace o šťastném čísle + Šťastné číslo pro dnešek + Dnes je šťastným číslem: %d + + Mobilní přístup + Žádná zařízení + Zrušit registraci + Zařízení odstraněno + QR kód + Token + Symbol + PIN + + Škola a učitelé + + Škola + Žádné informace o škole + Název školy + Adresa školy + Telefon + Jméno ředitele + Jméno pedagoga + Zobrazit na mapě + Volání + + Učitelé + Žádné informace o učitelích + Žádný předmět + + Setkání + Žádné informace o setkání + + Přidat účet + Odhlásit se + Chcete se odhlásit z aktivního studenta? + Odhlášení studentů + Studentský účet + Rodičovský účet + Režim Mobilního API + Hybridní režim + + Verze aplikace + Tvůrci + Seznam vývojářů Wulkanowy + Nahlásit chybu + Odeslat zprávu o chybě e-mailem + FAQ + Přečtěte si často kladené otázky + Server Discord + Připojte se ke komunitě Wulkanowy + Facebooková fanpage + Stejně jako naše facebooková fanpage + Zásady ochrany osobních údajů + Pravidla pro shromažďování osobních údajů + Domovská stránka + Navštivte web a pomozte s vývojem aplikace + Licence + Licence knihoven použitých v aplikaci + + Licence + + Avatar + Zobrazit více na GitHub + + Sdílejte protokoly + Obnovit + + Zkontrolovat aktualizace + Před hlášením chyby zkontrolujte, zda je k dispozici aktualizace s opravou chyb + + Obsah + Zkuste to znovu + Popis + Bez popisu + Učitel + Datum + Datum vstupu + Barva + Detaily + Kategorie + Zavřít + Žádná data + Předmět + Předchozí + Další + Hledat + Hledat… + + Žádné lekce + Vybrat motiv + Světlý + Tmavý + Motiv systému + + Vzhled + Výchozí zobrazení + Výpočet koncoročního průměru + Vynutit průměrný výpočet podle aplikace + Zobrazit přítomnost v účasti + Motiv aplikace + Rozbalit známky + Označte aktuální lekci v plánu lekce + Ukázat skupiny vedle předmětů v plánu lekce + Ukázat seznam grafů ve třídních známkách + Ukázat lekce pro celou třídu + Ukázat předměty bez známek v \"Známky\" + Známky barevné schéma + Předměty seřazené v \"Známky\" + Jazyk aplikace + Oznámení + Ukázat notifikace + Ukázat nadcházející oznámení o lekci + Opravte problémy se synchronizací a upozorněním + Ve vašem zařízení mohou nastat problémy se synchronizací dat a oznámenímii.\n\nChcete-li je opravit, přidejte Wulkanowy do funkce Autostart a vypněte optimalizaci/úsporu baterie v nastavení systému telefonu. + Přejít do nastavení + Ukázat oznámení o ladění + Synchronizace + Automatická aktualizace + Pozastaveno na dovolené + Interval aktualizací + Pouze Wi-Fi + Synchronizovat nyní + Synchronizováno! + Synchronizace selhala + Probíhá synchronizace + Synchronizace + Ruční synchronizace neobnoví zobrazení aplikace. + \nChcete-li zobrazit synchronizovaná data, restartujte aplikaci po synchronizaci. + + Jiné + Hodnota plusu + Hodnota mínusu + Odpovědět s historií zpráv + + Nové položky v deník + Nové známky + Šťastné číslo + Nové zprávy + Nové poznámky + Push oznámení + Nadcházející lekce + Debug + + Černý + Červený + Modrý + Zelený + Nachový + Žádná barva + + Zkopírováno + Vrátit + + Stahování aktualizací začalo… + Aktualizace byla stažena. + Restartovat + Aktualizace selhala! Wulkanowy nemusí fungovat správně. Zvažte aktualizaci + + Žádné internetové připojení + Nelze se připojit ke deníku. Servery mohou být přetíženy. Prosím zkuste to znovu později + Načítání dat se nezdařilo. Prosím zkuste to znovu později + Je vyžadována změna hesla pro deník + Probíhá údržba UONET+ deník. Zkuste to později znovu + Neznámá chyba denika UONET+. Prosím zkuste to znovu později + Neznámá chyba aplikace. Prosím zkuste to znovu později + Došlo k neočekávané chybě + Funkce deaktivována vaší školou + Funkce není k dispozici. Přihlaste se v jiném režimu než Mobile API + diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml index 11935b49b..7226ff2df 100644 --- a/app/src/main/res/values-de/preferences_values.xml +++ b/app/src/main/res/values-de/preferences_values.xml @@ -12,6 +12,7 @@ Pусский Українська Deutsch + Čeština 15 Minuten @@ -29,6 +30,10 @@ 0,5 0,75 + + Alphabetisch + Nach Datum + Dzienniczek+ Wulkanowy diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 7e2877674..7ba35734c 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -22,12 +22,12 @@ Semester %1$d, %2$d/%3$d Melden Sie sich mit dem Studenten- oder Elternkonto an - Geben Sie das Symbol + Geben Sie das Symbol von der Registerseite ein Benutzername Email Anmeldung, PESEL oder e-mail Passwort - UONET+ Klassenbuch + UONET+ Registervariante Mobile API Scraper Hybride @@ -37,17 +37,17 @@ Symbol Anmelden Passwort ist zu kurz - Anmeldedaten sind falsch. Stellen Sie sicher, dass das richtige UONET+-Register ausgewählt ist + Anmeldedaten sind falsch. Stellen Sie sicher, dass die richtige UONET+ Registervariation im unteren Feld ausgewählt ist Ungültige PIN Ungültige token Token ist nicht mehr gültig Ungültige email - Ungültige login + Den zugewiesenen Login anstelle von email verwenden Ungültige symbol - Student nicht gefunden. Überprüfen Sie das Symbol + Schüler nicht gefunden. Überprüfen Sie das Symbol und die gewählte Variation des UONET+ Registers Dieses Datenfeld ist erforderlich Ausgewählter Student ist bereits angemeldet. - Das Symbol finden Sie auf der Registerseite unter Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne + Das Symbol kann auf der Registerseite in Uczeń→ Dostęp Mobilny → Zarejestruj urządzenie mobilnegefunden werden.\n\nStellen Sie sicher, dass Sie die entsprechende Registervariante im Feld UONET+ Registervariante auf dem vorherigen Bildschirm festgelegt haben. Wulkanowy erkennt zur Zeit keine Vorschulstudenten Wählen Sie die Studenten aus, die sich bei der Anwendung anmelden sollen. Andere Optionen In diesem Modus funktioniert eine Glücknummer, eine Klassenstatistik, eine Zusammenfassung der Anwesenheit, eine Entschuldigung für die Abwesenheit, abgeschlossene Lektionen, Schulinformationen und eine Vorschau der Liste der registrierten Geräte nicht @@ -59,7 +59,7 @@ Discord email senden Beschreiben Sie die Details des Problems: - Stellen Sie sicher, dass das richtige UONET + Klassenbuch ausgewählt ist! + Stellen Sie sicher, dass Sie die richtige UONET+ Registervariation wählen! Ich habe mein Passwort vergessen. Ihr Konto wiederherstellen Wiederherstellen @@ -145,6 +145,10 @@ Thema Abwesenheit Ressourcen + + Additional lessons + Show additional lessons + No info about additional lessons Übersicht über die Schulbesuch Aus schulischen Gründen abwesend @@ -154,6 +158,8 @@ Entschuldigte Verspätung Unentschuldigte Verspätung Anwesend + Entfernt + Unbekannt Lektion Nummer Keine Einträgen @@ -193,6 +199,7 @@ Thema Inhalt Nachricht erfolgreich gesendet + Message does not exist Sie müssen mindestens 1 Empfänger auswählen. Der Inhalt der Nachricht muss mindestens 3 Zeichen lang sein. @@ -222,6 +229,32 @@ Du hast %1$d Eintrag bekommen Du hast %1$d Eintragen bekommen + + + %d Lob + %d Lob + + + Neues Lob + Neues Lob + + + Du hast %1$d Lob erhalten + Du hast %1$d Lob erhalten + + + + %d neutrale Notiz + %d neutrale Notizen + + + Neue neutrale Notiz + Neue neutrale Notizen + + + Du hast %1$d neutrale Notiz erhalten + Du hast %1$d neutrale Notizen erhalten + Keine Informationen über Hausaufgaben Gemacht @@ -258,6 +291,9 @@ Lehrerinnen und Lehrer Keine Informationen über Lehrer Kein Thema + + Sitzungen + Keine Informationen über Sitzungen Konto hinzufügen Abmelden @@ -277,6 +313,8 @@ Lesen Sie Häufig gestellte Fragen Discord server Treten Sie der Wulkanowy-Gemeinschaft bei + Facebook fanpage + Gefällt unsere Facebook-Fanpage Datenschutzerklärung Regeln für die Sammlung persönlicher Daten Startseite @@ -327,9 +365,12 @@ Thema der Anwendung Noten erweitern Aktuelle Lektion im Stundenplan markieren + Zeige Gruppen neben Themen im Zeitplan Liste der Diagramme in Klassenbewertungen anzeigen Unterricht der ganzen Klasse anzeigen + Zeigen Sie Themen ohne Noten in Noten Farbschema der Noten + Themen sortieren in \"Noten\" App Sprache Benachrichtigungen Benachrichtigungen anzeigen @@ -374,12 +415,19 @@ Kopiert lösen + + Download der Updates wurde gestartet… + Ein Update wurde gerade heruntergeladen. + Neustart + Update fehlgeschlagen! Wulkanowy funktioniert möglicherweise nicht richtig. Überlegen Sie die Aktualisierung Keine Internetverbindung - Das Zeitlimit für die Verbindung zum Klassenbuch ist abgelaufen - Anmeldung fehlgeschlagen. Versuchen Sie es noch einmal oder starten Sie die Anwendung neu. - Passwortänderung erforderlich + Registrierungsverbindung fehlgeschlagen. Server können überlastet sein. Bitte versuchen Sie es später noch einmal + Das Laden der Daten ist fehlgeschlagen. Bitte versuchen Sie es später noch einmal + Passwortänderung für Registrierung erforderlich Wartung im Gange UONET + Klassenbuch. Versuchen Sie es später noch einmal + Unbekannter UONET + Registerfehler. Versuchen Sie es später erneut + Unbekannter Anwendungsfehler. Bitte versuchen Sie es später noch einmal Ein unerwarteter Fehler ist aufgetreten Funktion, die von Ihrer Schule deaktiviert wurde Feature in diesem Modus nicht verfügbar diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index a06a46f00..32f7cdf13 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -1,6 +1,7 @@ - -