diff --git a/.travis.yml b/.travis.yml index cc45f7dbe..983f521ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ cache: branches: only: - develop - - 0.16.0 + - 0.17.3 android: licenses: diff --git a/app/build.gradle b/app/build.gradle index 86fe69666..b8e2ee207 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,7 @@ apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-android-extensions' apply plugin: 'io.fabric' apply plugin: 'com.github.triplet.play' +apply plugin: 'com.mikepenz.aboutlibraries.plugin' apply from: 'jacoco.gradle' apply from: 'sonarqube.gradle' apply from: 'hooks.gradle' @@ -17,8 +18,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 17 targetSdkVersion 29 - versionCode 53 - versionName "0.16.0" + versionCode 56 + versionName "0.17.2" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -96,6 +97,10 @@ android { exclude 'META-INF/library_release.kotlin_module' exclude 'META-INF/library-core_release.kotlin_module' } + + aboutLibraries { + configPath = "app/src/main/res/raw" + } } androidExtensions { @@ -110,10 +115,11 @@ play { } ext { - work_manager = "2.3.2" - room = "2.2.4" - dagger = "2.26" - chucker = "2.0.4" + work_manager = "2.3.4" + room = "2.2.5" + dagger = "2.27" + // don't update https://github.com/ChuckerTeam/chucker/issues/242 + chucker = "3.2.0" mockk = "1.9.2" } @@ -122,21 +128,21 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:0.16.0" + implementation "io.github.wulkanowy:sdk:0.17.3" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0" implementation "androidx.activity:activity-ktx:1.1.0" - implementation "androidx.appcompat:appcompat:1.1.0" + implementation "androidx.appcompat:appcompat:1.2.0-beta01" implementation "androidx.appcompat:appcompat-resources:1.1.0" - implementation "androidx.fragment:fragment-ktx:1.2.2" + implementation "androidx.fragment:fragment-ktx:1.2.4" implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.multidex:multidex:2.0.1" - implementation "androidx.preference:preference-ktx:1.1.0" + implementation "androidx.preference:preference-ktx:1.1.1" implementation "androidx.recyclerview:recyclerview:1.1.0" implementation "androidx.viewpager:viewpager:1.0.0" - implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03" + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-rc01" implementation "androidx.constraintlayout:constraintlayout:1.1.3" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" implementation "com.google.android.material:material:1.1.0" @@ -148,6 +154,8 @@ dependencies { implementation "androidx.work:work-rxjava2:$work_manager" implementation "androidx.work:work-gcm:$work_manager" + implementation 'com.github.PaulinaSadowska:RxWorkManagerObservers:1.0.0' + implementation "androidx.room:room-runtime:$room" implementation "androidx.room:room-rxjava2:$room" implementation "androidx.room:room-ktx:$room" @@ -163,34 +171,37 @@ dependencies { implementation "eu.davidea:flexible-adapter-ui:1.0.0" implementation "com.aurelhubert:ahbottomnavigation:2.3.4" implementation "com.ncapdevi:frag-nav:3.3.0" - implementation "com.github.YarikSOffice:lingver:1.1.0" + implementation "com.github.YarikSOffice:lingver:1.2.1" implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6" implementation "io.reactivex.rxjava2:rxandroid:2.1.1" - implementation "io.reactivex.rxjava2:rxjava:2.2.18" + implementation "io.reactivex.rxjava2:rxjava:2.2.19" implementation "com.google.code.gson:gson:2.8.6" - implementation "com.jakewharton.threetenabp:threetenabp:1.2.2" + implementation "com.jakewharton.threetenabp:threetenabp:1.2.3" implementation "com.jakewharton.timber:timber:4.7.1" implementation "at.favre.lib:slf4j-timber:1.0.1" implementation "fr.bipi.treessence:treessence:0.3.2" - implementation "com.mikepenz:aboutlibraries-core:7.1.0" + implementation "com.mikepenz:aboutlibraries-core:$about_libraries" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' + implementation "io.coil-kt:coil:0.9.5" - implementation("io.coil-kt:coil:0.9.5") - - playImplementation "com.google.firebase:firebase-core:17.2.3" + playImplementation 'com.google.firebase:firebase-analytics:17.3.0' + playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.0.5' + playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.0.5" + playImplementation "com.google.firebase:firebase-messaging:20.1.0" playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1" + playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' - releaseImplementation "fr.o80.chucker:library-no-op:$chucker" + releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" - debugImplementation "fr.o80.chucker:library:$chucker" + debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker" debugImplementation "com.amitshekhar.android:debug-db:1.0.6" testImplementation "junit:junit:4.13" testImplementation "io.mockk:mockk:$mockk" - testImplementation "org.threeten:threetenbp:1.4.1" - testImplementation "org.mockito:mockito-inline:3.3.1" + testImplementation "org.threeten:threetenbp:1.4.3" + testImplementation "org.mockito:mockito-inline:3.3.3" androidTestImplementation "androidx.test:core:1.2.0" androidTestImplementation "androidx.test:runner:1.2.0" @@ -198,7 +209,7 @@ dependencies { androidTestImplementation "io.mockk:mockk-android:$mockk" androidTestImplementation "androidx.room:room-testing:$room" androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - androidTestImplementation "org.mockito:mockito-android:3.3.1" + androidTestImplementation "org.mockito:mockito-android:3.3.3" } apply plugin: 'com.google.gms.google-services' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 77339fe9e..1a8b8c329 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -43,3 +43,10 @@ #Config for Material Components -keep class com.google.android.material.tabs.** { *; } + + +#Config for About Libraries +-keep class .R +-keep class **.R$* { + ; +} diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/23.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/23.json new file mode 100644 index 000000000..f75a72acf --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/23.json @@ -0,0 +1,1682 @@ +{ + "formatVersion": 1, + "database": { + "version": 23, + "identityHash": "f2505bf0c76c3ae4567856536d790909", + "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, `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": "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, `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": "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, `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, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` 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": "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": "content", + "columnName": "content", + "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": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "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, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "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 + } + ], + "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": [] + } + ], + "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, 'f2505bf0c76c3ae4567856536d790909')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/24.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/24.json new file mode 100644 index 000000000..17ae7d798 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/24.json @@ -0,0 +1,1732 @@ +{ + "formatVersion": 1, + "database": { + "version": 24, + "identityHash": "9bbf60310b56a855839164e2aae031f9", + "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, `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": "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, `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": "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, `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, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` 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": "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": "content", + "columnName": "content", + "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": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "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, `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)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "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 + } + ], + "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": [] + } + ], + "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, '9bbf60310b56a855839164e2aae031f9')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/25.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/25.json new file mode 100644 index 000000000..e99a11d4a --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/25.json @@ -0,0 +1,1744 @@ +{ + "formatVersion": 1, + "database": { + "version": 25, + "identityHash": "d101f5a26a024f62e6fee161e421b882", + "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, `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": "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, `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": "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, `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, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` 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": "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": "content", + "columnName": "content", + "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": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "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": [] + } + ], + "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, 'd101f5a26a024f62e6fee161e421b882')" + ] + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..bbbfd83dd --- /dev/null +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/TestEntityCreator.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.data.repositories + +import io.github.wulkanowy.data.db.entities.Student +import org.threeten.bp.LocalDateTime + +fun getStudent(): Student { + return Student( + email = "test", + password = "test123", + schoolSymbol = "23", + scrapperBaseUrl = "fakelog.cf", + loginType = "AUTO", + isCurrent = true, + studentName = "", + schoolShortName = "", + schoolName = "", + studentId = 0, + classId = 1, + symbol = "", + registrationDate = LocalDateTime.now(), + className = "", + loginMode = "API", + certificateKey = "", + privateKey = "", + mobileBaseUrl = "", + userLoginId = 0, + isParent = false + ) +} 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 index 5f8be6b15..530bfb3f7 100644 --- 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 @@ -5,13 +5,11 @@ 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.SharedPrefProvider -import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.getStudent import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.threeten.bp.LocalDateTime.now import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) @@ -21,14 +19,13 @@ class StudentLocalTest { private lateinit var testDb: AppDatabase - private lateinit var sharedProvider: SharedPrefProvider + private val student = getStudent() @Before fun createDb() { val context = ApplicationProvider.getApplicationContext() testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) .build() - sharedProvider = SharedPrefProvider(context.getSharedPreferences("TEST", Context.MODE_PRIVATE)) studentLocal = StudentLocal(testDb.studentDao, context) } @@ -39,8 +36,7 @@ class StudentLocalTest { @Test fun saveAndReadTest() { - studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", scrapperBaseUrl = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolShortName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "", loginMode = "API", certificateKey = "", privateKey = "", mobileBaseUrl = "", userLoginId = 0, isParent = false))) - .blockingGet() + studentLocal.saveStudents(listOf(student)).blockingGet() val student = studentLocal.getCurrentStudent(true).blockingGet() assertEquals("23", student.schoolSymbol) 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 index 565425d43..fdf193a26 100644 --- 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 @@ -9,6 +9,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy +import io.github.wulkanowy.data.repositories.getStudent import io.github.wulkanowy.sdk.Sdk import io.mockk.MockKAnnotations import io.mockk.every @@ -33,6 +34,8 @@ class TimetableRepositoryTest { .strategy(TestInternetObservingStrategy()) .build() + private val student = getStudent() + @MockK private lateinit var semesterMock: Semester @@ -78,7 +81,7 @@ class TimetableRepositoryTest { )) val lessons = TimetableRepository(settings, timetableLocal, timetableRemote) - .getTimetable(semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true) + .getTimetable(student, semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true) .blockingGet() assertEquals(4, lessons.size) @@ -124,7 +127,7 @@ class TimetableRepositoryTest { )) val lessons = TimetableRepository(settings, timetableLocal, timetableRemote) - .getTimetable(semesterMock, LocalDate.of(2019, 12, 23), LocalDate.of(2019, 12, 25), true) + .getTimetable(student, semesterMock, LocalDate.of(2019, 12, 23), LocalDate.of(2019, 12, 25), true) .blockingGet() assertEquals(12, lessons.size) diff --git a/app/src/debug/res/values/preferences_defaults.xml b/app/src/debug/res/values/preferences_defaults.xml new file mode 100644 index 000000000..f5a12464f --- /dev/null +++ b/app/src/debug/res/values/preferences_defaults.xml @@ -0,0 +1,4 @@ + + + true + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 694c8053a..42c754756 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,7 +22,8 @@ + android:theme="@style/WulkanowyTheme.SplashScreen" + tools:ignore="LockedOrientationActivity"> @@ -112,5 +113,13 @@ + + + + diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt index 48e51e3ce..43c27c529 100644 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt @@ -7,9 +7,9 @@ import android.content.res.Resources import androidx.preference.PreferenceManager import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy -import com.readystatesoftware.chuck.api.ChuckCollector -import com.readystatesoftware.chuck.api.ChuckInterceptor -import com.readystatesoftware.chuck.api.RetentionManager +import com.chuckerteam.chucker.api.ChuckerCollector +import com.chuckerteam.chucker.api.ChuckerInterceptor +import com.chuckerteam.chucker.api.RetentionManager import dagger.Module import dagger.Provides import io.github.wulkanowy.data.db.AppDatabase @@ -32,23 +32,25 @@ internal class RepositoryModule { @Singleton @Provides - fun provideSdk(chuckCollector: ChuckCollector, context: Context): Sdk { + fun provideSdk(chuckerCollector: ChuckerCollector, context: Context): Sdk { return Sdk().apply { androidVersion = android.os.Build.VERSION.RELEASE buildTag = android.os.Build.MODEL setSimpleHttpLogger { Timber.d(it) } // for debug only - addInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true) + addInterceptor(ChuckerInterceptor(context, chuckerCollector), true) } } @Singleton @Provides - fun provideChuckCollector(context: Context, prefRepository: PreferencesRepository): ChuckCollector { - return ChuckCollector(context) - .showNotification(prefRepository.isDebugNotificationEnable) - .retentionManager(RetentionManager(context, ChuckCollector.Period.ONE_HOUR)) + fun provideChuckerCollector(context: Context, prefRepository: PreferencesRepository): ChuckerCollector { + return ChuckerCollector( + context = context, + showNotification = prefRepository.isDebugNotificationEnable, + retentionPeriod = RetentionManager.Period.ONE_HOUR + ) } @Singleton @@ -95,6 +97,10 @@ internal class RepositoryModule { @Provides fun provideMessagesDao(database: AppDatabase) = database.messagesDao + @Singleton + @Provides + fun provideMessageAttachmentsDao(database: AppDatabase) = database.messageAttachmentDao + @Singleton @Provides fun provideExamDao(database: AppDatabase) = database.examsDao diff --git a/app/src/main/java/io/github/wulkanowy/data/SdkHelper.kt b/app/src/main/java/io/github/wulkanowy/data/SdkHelper.kt deleted file mode 100644 index 901712594..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/SdkHelper.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.wulkanowy.data - -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import javax.inject.Inject - -class SdkHelper @Inject constructor(private val sdk: Sdk) { - - fun init(student: Student) { - sdk.apply { - email = student.email - password = student.password - symbol = student.symbol - schoolSymbol = student.schoolSymbol - studentId = student.studentId - classId = student.classId - - if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { - scrapperBaseUrl = student.scrapperBaseUrl - loginType = Sdk.ScrapperLoginType.valueOf(student.loginType) - } - loginId = student.userLoginId - - mode = Sdk.Mode.valueOf(student.loginMode) - mobileBaseUrl = student.mobileBaseUrl - certKey = student.certificateKey - privateKey = student.privateKey - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 4eaf89e59..762c52f8f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -17,6 +17,7 @@ import io.github.wulkanowy.data.db.dao.GradeStatisticsDao import io.github.wulkanowy.data.db.dao.GradeSummaryDao import io.github.wulkanowy.data.db.dao.HomeworkDao import io.github.wulkanowy.data.db.dao.LuckyNumberDao +import io.github.wulkanowy.data.db.dao.MessageAttachmentDao import io.github.wulkanowy.data.db.dao.MessagesDao import io.github.wulkanowy.data.db.dao.MobileDeviceDao import io.github.wulkanowy.data.db.dao.NoteDao @@ -39,6 +40,7 @@ import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageAttachment import io.github.wulkanowy.data.db.entities.MobileDevice import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Recipient @@ -63,6 +65,9 @@ import io.github.wulkanowy.data.db.migrations.Migration2 import io.github.wulkanowy.data.db.migrations.Migration20 import io.github.wulkanowy.data.db.migrations.Migration21 import io.github.wulkanowy.data.db.migrations.Migration22 +import io.github.wulkanowy.data.db.migrations.Migration23 +import io.github.wulkanowy.data.db.migrations.Migration24 +import io.github.wulkanowy.data.db.migrations.Migration25 import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration4 import io.github.wulkanowy.data.db.migrations.Migration5 @@ -86,6 +91,7 @@ import javax.inject.Singleton GradeStatistics::class, GradePointsStatistics::class, Message::class, + MessageAttachment::class, Note::class, Homework::class, Subject::class, @@ -104,7 +110,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 22 + const val VERSION_SCHEMA = 25 fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array { return arrayOf( @@ -128,7 +134,10 @@ abstract class AppDatabase : RoomDatabase() { Migration19(sharedPrefProvider), Migration20(), Migration21(), - Migration22() + Migration22(), + Migration23(), + Migration24(), + Migration25() ) } @@ -164,6 +173,8 @@ abstract class AppDatabase : RoomDatabase() { abstract val messagesDao: MessagesDao + abstract val messageAttachmentDao: MessageAttachmentDao + abstract val noteDao: NoteDao abstract val homeworkDao: HomeworkDao diff --git a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt index 73a04d236..294f73d3d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt @@ -48,4 +48,14 @@ class Converters { fun gsonToIntList(value: String): List { return Gson().fromJson(value, object : TypeToken>() {}.type) } + + @TypeConverter + fun stringPairListToGson(list: List>): String { + return Gson().toJson(list) + } + + @TypeConverter + fun gsonToStringPairList(value: String): List> { + return Gson().fromJson(value, object : TypeToken>>() {}.type) + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessageAttachmentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessageAttachmentDao.kt new file mode 100644 index 000000000..3c511a277 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessageAttachmentDao.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import io.github.wulkanowy.data.db.entities.MessageAttachment + +@Dao +interface MessageAttachmentDao : BaseDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertAttachments(items: List): List +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt index 40ba0fe71..7a69e2707 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt @@ -2,19 +2,22 @@ package io.github.wulkanowy.data.db.dao import androidx.room.Dao import androidx.room.Query +import androidx.room.Transaction import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.reactivex.Maybe import io.reactivex.Single @Dao interface MessagesDao : BaseDao { + @Transaction + @Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId") + fun loadMessageWithAttachment(studentId: Int, messageId: Int): Single + @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC") fun loadAll(studentId: Int, folder: Int): Maybe> - @Query("SELECT * FROM Messages WHERE id = :id") - fun load(id: Long): Single - @Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC") fun loadDeleted(studentId: Int): Maybe> } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt index 57bf25fb8..901ddc73f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt @@ -22,6 +22,9 @@ interface StudentDao { @Query("SELECT * FROM Students WHERE is_current = 1") fun loadCurrent(): Maybe + @Query("SELECT * FROM Students WHERE id = :id") + fun loadById(id: Int): Maybe + @Query("SELECT * FROM Students") fun loadAll(): Maybe> diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt index a22df0961..cd7d153e9 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Homework.kt @@ -27,10 +27,14 @@ data class Homework( val teacher: String, @ColumnInfo(name = "teacher_symbol") - val teacherSymbol: String + val teacherSymbol: String, + val attachments: List> ) : Serializable { @PrimaryKey(autoGenerate = true) var id: Long = 0 + + @ColumnInfo(name = "is_done") + var isDone: Boolean = false } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt index 93e254c3b..058298415 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt @@ -44,7 +44,10 @@ data class Message( @ColumnInfo(name = "read_by") val readBy: Int, - val removed: Boolean + val removed: Boolean, + + @ColumnInfo(name = "has_attachments") + val hasAttachments: Boolean ) : Serializable { @PrimaryKey(autoGenerate = true) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt new file mode 100644 index 000000000..d1886e910 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt @@ -0,0 +1,26 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.io.Serializable + +@Entity(tableName = "MessageAttachments") +data class MessageAttachment( + + @PrimaryKey + @ColumnInfo(name = "real_id") + val realId: Int, + + @ColumnInfo(name = "message_id") + val messageId: Int, + + @ColumnInfo(name = "one_drive_id") + val oneDriveId: String, + + @ColumnInfo(name = "url") + val url: String, + + @ColumnInfo(name = "filename") + val filename: String +) : Serializable diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt new file mode 100644 index 000000000..2e7af0f40 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.db.entities + +import androidx.room.Embedded +import androidx.room.Relation + +data class MessageWithAttachment( + @Embedded + val message: Message, + + @Relation(parentColumn = "message_id", entityColumn = "message_id") + val attachments: List +) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt index 5f3a92ab6..6f707a66a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Note.kt @@ -16,8 +16,19 @@ data class Note( val teacher: String, + @ColumnInfo(name = "teacher_symbol") + val teacherSymbol: String, + val category: String, + @ColumnInfo(name = "category_type") + val categoryType: Int, + + @ColumnInfo(name = "is_points_show") + val isPointsShow: Boolean, + + val points: Int, + val content: String ) : Serializable { diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration23.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration23.kt new file mode 100644 index 000000000..22de94c3f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration23.kt @@ -0,0 +1,14 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration23 : Migration(22, 23) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Notes ADD COLUMN teacher_symbol TEXT NOT NULL DEFAULT ''") + database.execSQL("ALTER TABLE Notes ADD COLUMN category_type INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Notes ADD COLUMN is_points_show INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Notes ADD COLUMN points INTEGER NOT NULL DEFAULT 0") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration24.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration24.kt new file mode 100644 index 000000000..604ed4875 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration24.kt @@ -0,0 +1,21 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration24 : Migration(23, 24) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Messages ADD COLUMN has_attachments INTEGER NOT NULL DEFAULT 0") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS MessageAttachments ( + 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) + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration25.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration25.kt new file mode 100644 index 000000000..4749bac73 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration25.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration25 : Migration(24, 25) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Homework ADD COLUMN is_done INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Homework ADD COLUMN attachments TEXT NOT NULL DEFAULT \"[]\"") + } +} 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 index 1828c2bdf..f64d920db 100644 --- 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 @@ -2,8 +2,10 @@ 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 io.reactivex.Single import org.threeten.bp.LocalDate import org.threeten.bp.LocalDateTime @@ -14,8 +16,9 @@ import javax.inject.Singleton @Singleton class AttendanceRemote @Inject constructor(private val sdk: Sdk) { - fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getAttendance(startDate, endDate, semester.semesterId) + fun getAttendance(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getAttendance(startDate, endDate, semester.semesterId) .map { attendance -> attendance.map { Attendance( @@ -39,8 +42,8 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) { } } - fun excuseAbsence(semester: Semester, absenceList: List, reason: String?): Single { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance -> + fun excuseAbsence(student: Student, semester: Semester, absenceList: List, reason: String?): Single { + 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 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 index fc3830c2e..22fe7fa51 100644 --- 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 @@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.friday import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.uniqueSubtract @@ -20,29 +21,25 @@ class AttendanceRepository @Inject constructor( private val remote: AttendanceRemote ) { - fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean) - : Single> { - return Single.fromCallable { startDate.monday to endDate.friday } - .flatMap { dates -> - local.getAttendance(semester, dates.first, dates.second).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap { - if (it) remote.getAttendance(semester, dates.first, dates.second) - else Single.error(UnknownHostException()) - }.flatMap { newAttendance -> - local.getAttendance(semester, dates.first, dates.second) - .toSingle(emptyList()) - .doOnSuccess { oldAttendance -> - local.deleteAttendance(oldAttendance.uniqueSubtract(newAttendance)) - local.saveAttendance(newAttendance.uniqueSubtract(oldAttendance)) - } - }.flatMap { - local.getAttendance(semester, dates.first, dates.second) - .toSingle(emptyList()) - }).map { list -> list.filter { it.date in startDate..endDate } } - } + fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean): Single> { + return local.getAttendance(semester, start.monday, end.friday).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap { + if (it) remote.getAttendance(student, semester, start.monday, end.friday) + else Single.error(UnknownHostException()) + }.flatMap { newAttendance -> + local.getAttendance(semester, start.monday, end.friday) + .toSingle(emptyList()) + .doOnSuccess { oldAttendance -> + local.deleteAttendance(oldAttendance.uniqueSubtract(newAttendance)) + local.saveAttendance(newAttendance.uniqueSubtract(oldAttendance)) + } + }.flatMap { + local.getAttendance(semester, start.monday, end.friday) + .toSingle(emptyList()) + }).map { list -> list.filter { it.date in start..end } } } - fun excuseForAbsence(semester: Semester, attendanceList: List, reason: String? = null): Single { - return remote.excuseAbsence(semester, attendanceList, reason) + fun excuseForAbsence(student: Student, semester: Semester, attendanceList: List, reason: String? = null): Single { + return remote.excuseAbsence(student, semester, attendanceList, reason) } } 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 index c167427f7..8fef5c391 100644 --- 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 @@ -2,7 +2,9 @@ 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 io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -10,8 +12,9 @@ import javax.inject.Singleton @Singleton class AttendanceSummaryRemote @Inject constructor(private val sdk: Sdk) { - fun getAttendanceSummary(semester: Semester, subjectId: Int): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getAttendanceSummary(subjectId) + fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getAttendanceSummary(subjectId) .map { attendance -> attendance.map { AttendanceSummary( 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 index c65870508..5f0b2b346 100644 --- 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 @@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.utils.uniqueSubtract import io.reactivex.Single import java.net.UnknownHostException @@ -17,11 +18,11 @@ class AttendanceSummaryRepository @Inject constructor( private val remote: AttendanceSummaryRemote ) { - fun getAttendanceSummary(semester: Semester, subjectId: Int, forceRefresh: Boolean = false): Single> { + fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean = false): Single> { return local.getAttendanceSummary(semester, subjectId).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getAttendanceSummary(semester, subjectId) + if (it) remote.getAttendanceSummary(student, semester, subjectId) else Single.error(UnknownHostException()) }.flatMap { new -> local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) 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 index ca680f209..bb115111e 100644 --- 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 @@ -2,7 +2,9 @@ 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 io.reactivex.Single import org.threeten.bp.LocalDate import javax.inject.Inject @@ -11,8 +13,9 @@ import javax.inject.Singleton @Singleton class CompletedLessonsRemote @Inject constructor(private val sdk: Sdk) { - fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getCompletedLessons(startDate, endDate) + fun getCompletedLessons(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getCompletedLessons(startDate, endDate) .map { lessons -> lessons.map { it.absence 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 index c22fabc39..f31e30552 100644 --- 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 @@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.utils.friday import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.uniqueSubtract @@ -20,25 +21,22 @@ class CompletedLessonsRepository @Inject constructor( private val remote: CompletedLessonsRemote ) { - fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single> { - return Single.fromCallable { startDate.monday to endDate.friday } - .flatMap { dates -> - local.getCompletedLessons(semester, dates.first, dates.second).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getCompletedLessons(semester, dates.first, dates.second) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getCompletedLessons(semester, dates.first, dates.second) - .toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteCompleteLessons(old.uniqueSubtract(new)) - local.saveCompletedLessons(new.uniqueSubtract(old)) - } - }.flatMap { - local.getCompletedLessons(semester, dates.first, dates.second) - .toSingle(emptyList()) - }).map { list -> list.filter { it.date in startDate..endDate } } - } + fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> { + return local.getCompletedLessons(semester, start.monday, end.friday).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getCompletedLessons(student, semester, start.monday, end.friday) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getCompletedLessons(semester, start.monday, end.friday) + .toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteCompleteLessons(old.uniqueSubtract(new)) + local.saveCompletedLessons(new.uniqueSubtract(old)) + } + }.flatMap { + local.getCompletedLessons(semester, start.monday, end.friday) + .toSingle(emptyList()) + }).map { list -> list.filter { it.date in start..end } } } } 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 index ea7ec9441..fb105ceee 100644 --- 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 @@ -2,7 +2,9 @@ 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 io.reactivex.Single import org.threeten.bp.LocalDate import javax.inject.Inject @@ -11,8 +13,9 @@ import javax.inject.Singleton @Singleton class ExamRemote @Inject constructor(private val sdk: Sdk) { - fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getExams(startDate, endDate, semester.semesterId) + fun getExams(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getExams(startDate, endDate, semester.semesterId) .map { exams -> exams.map { Exam( 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 index be60eaecd..452a15510 100644 --- 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 @@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.utils.friday import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.uniqueSubtract @@ -20,25 +21,22 @@ class ExamRepository @Inject constructor( private val remote: ExamRemote ) { - fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate, forceRefresh: Boolean = false): Single> { - return Single.fromCallable { startDate.monday to endDate.friday } - .flatMap { dates -> - local.getExams(semester, dates.first, dates.second).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getExams(semester, dates.first, dates.second) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getExams(semester, dates.first, dates.second) - .toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteExams(old.uniqueSubtract(new)) - local.saveExams(new.uniqueSubtract(old)) - } - }.flatMap { - local.getExams(semester, dates.first, dates.second) - .toSingle(emptyList()) - }).map { list -> list.filter { it.date in startDate..endDate } } - } + fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> { + return local.getExams(semester, start.monday, end.friday).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getExams(student, semester, start.monday, end.friday) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getExams(semester, start.monday, end.friday) + .toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteExams(old.uniqueSubtract(new)) + local.saveExams(new.uniqueSubtract(old)) + } + }.flatMap { + local.getExams(semester, start.monday, end.friday) + .toSingle(emptyList()) + }).map { list -> list.filter { it.date in start..end } } } } 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 index d07ac6d1c..6f19095ba 100644 --- 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 @@ -2,7 +2,9 @@ package io.github.wulkanowy.data.repositories.grade 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.github.wulkanowy.utils.init import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -10,8 +12,9 @@ import javax.inject.Singleton @Singleton class GradeRemote @Inject constructor(private val sdk: Sdk) { - fun getGrades(semester: Semester): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGrades(semester.semesterId) + fun getGrades(student: Student, semester: Semester): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getGrades(semester.semesterId) .map { grades -> grades.map { Grade( 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/grade/GradeRepository.kt index 7f8e92a2e..351ebc392 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRepository.kt @@ -23,7 +23,7 @@ class GradeRepository @Inject constructor( return local.getGrades(semester).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getGrades(semester) + if (it) remote.getGrades(student, semester) else Single.error(UnknownHostException()) }.flatMap { new -> local.getGrades(semester).toSingle(emptyList()) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt index 2681fe058..1b09974dd 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt @@ -2,7 +2,9 @@ package io.github.wulkanowy.data.repositories.gradessummary 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 io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -10,8 +12,9 @@ import javax.inject.Singleton @Singleton class GradeSummaryRemote @Inject constructor(private val sdk: Sdk) { - fun getGradeSummary(semester: Semester): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGradesSummary(semester.semesterId) + fun getGradeSummary(student: Student, semester: Semester): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getGradesSummary(semester.semesterId) .map { gradesSummary -> gradesSummary.map { GradeSummary( diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt index 660a032c2..f1d7a6c1c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRepository.kt @@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.utils.uniqueSubtract import io.reactivex.Single import java.net.UnknownHostException @@ -17,11 +18,11 @@ class GradeSummaryRepository @Inject constructor( private val remote: GradeSummaryRemote ) { - fun getGradesSummary(semester: Semester, forceRefresh: Boolean = false): Single> { + fun getGradesSummary(student: Student, semester: Semester, forceRefresh: Boolean = false): Single> { return local.getGradesSummary(semester).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getGradeSummary(semester) + if (it) remote.getGradeSummary(student, semester) else Single.error(UnknownHostException()) }.flatMap { new -> local.getGradesSummary(semester).toSingle(emptyList()) 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 index f10f30369..99e0cb987 100644 --- 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 @@ -3,7 +3,9 @@ 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 io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -11,8 +13,8 @@ import javax.inject.Singleton @Singleton class GradeStatisticsRemote @Inject constructor(private val sdk: Sdk) { - fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).let { + fun getGradeStatistics(student: Student, semester: Semester, isSemester: Boolean): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).let { if (isSemester) it.getGradesAnnualStatistics(semester.semesterId) else it.getGradesPartialStatistics(semester.semesterId) }.map { gradeStatistics -> @@ -29,8 +31,9 @@ class GradeStatisticsRemote @Inject constructor(private val sdk: Sdk) { } } - fun getGradePointsStatistics(semester: Semester): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGradesPointsStatistics(semester.semesterId) + fun getGradePointsStatistics(student: Student, semester: Semester): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getGradesPointsStatistics(semester.semesterId) .map { gradePointsStatistics -> gradePointsStatistics.map { GradePointsStatistics( 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 index e82e9db4d..8d96d2f57 100644 --- 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 @@ -5,6 +5,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.Inter 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.uniqueSubtract @@ -20,11 +21,11 @@ class GradeStatisticsRepository @Inject constructor( private val remote: GradeStatisticsRemote ) { - fun getGradesStatistics(semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single> { + fun getGradesStatistics(student: Student, semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): Single> { return local.getGradesStatistics(semester, isSemester, subjectName).map { it.mapToStatisticItems() }.filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getGradeStatistics(semester, isSemester) + if (it) remote.getGradeStatistics(student, semester, isSemester) else Single.error(UnknownHostException()) }.flatMap { new -> local.getGradesStatistics(semester, isSemester).toSingle(emptyList()) @@ -35,11 +36,11 @@ class GradeStatisticsRepository @Inject constructor( }.flatMap { local.getGradesStatistics(semester, isSemester, subjectName).map { it.mapToStatisticItems() }.toSingle(emptyList()) }) } - fun getGradesPointsStatistics(semester: Semester, subjectName: String, forceRefresh: Boolean): Single> { + fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean): Single> { return local.getGradesPointsStatistics(semester, subjectName).map { it.mapToStatisticsItem() }.filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getGradePointsStatistics(semester) + if (it) remote.getGradePointsStatistics(student, semester) else Single.error(UnknownHostException()) }.flatMap { new -> local.getGradesPointsStatistics(semester).toSingle(emptyList()) 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 index 671ecafd7..fdae45182 100644 --- 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 @@ -19,6 +19,10 @@ class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) { homeworkDb.deleteAll(homework) } + fun updateHomework(homework: List) { + homeworkDb.updateAll(homework) + } + fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe> { return homeworkDb.loadAll(semester.semesterId, semester.studentId, startDate, endDate) .filter { it.isNotEmpty() } 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 index 189499dbe..20ffee99c 100644 --- 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 @@ -2,7 +2,9 @@ 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 io.reactivex.Single import org.threeten.bp.LocalDate import javax.inject.Inject @@ -11,8 +13,9 @@ import javax.inject.Singleton @Singleton class HomeworkRemote @Inject constructor(private val sdk: Sdk) { - fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getHomework(startDate, endDate) + fun getHomework(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getHomework(startDate, endDate) .map { homework -> homework.map { Homework( @@ -23,7 +26,8 @@ class HomeworkRemote @Inject constructor(private val sdk: Sdk) { subject = it.subject, content = it.content, teacher = it.teacher, - teacherSymbol = it.teacherSymbol + 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 index 3f924944d..4454fd88e 100644 --- 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 @@ -4,9 +4,11 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.friday import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.uniqueSubtract +import io.reactivex.Completable import io.reactivex.Single import org.threeten.bp.LocalDate import java.net.UnknownHostException @@ -20,12 +22,12 @@ class HomeworkRepository @Inject constructor( private val remote: HomeworkRemote ) { - fun getHomework(semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> { + fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> { return Single.fromCallable { start.monday to end.friday }.flatMap { (monday, friday) -> local.getHomework(semester, monday, friday).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getHomework(semester, monday, friday) + if (it) remote.getHomework(student, semester, monday, friday) else Single.error(UnknownHostException()) }.flatMap { new -> local.getHomework(semester, monday, friday).toSingle(emptyList()) @@ -36,4 +38,12 @@ class HomeworkRepository @Inject constructor( }.flatMap { local.getHomework(semester, monday, friday).toSingle(emptyList()) }) } } + + fun toggleDone(homework: Homework): Completable { + return Completable.fromCallable { + local.updateHomework(listOf(homework.apply { + isDone = !isDone + })) + } + } } 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 index 2fcf9338b..0c71897a6 100644 --- 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 @@ -3,6 +3,7 @@ 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 io.reactivex.Maybe import org.threeten.bp.LocalDate import javax.inject.Inject @@ -12,7 +13,7 @@ import javax.inject.Singleton class LuckyNumberRemote @Inject constructor(private val sdk: Sdk) { fun getLuckyNumber(student: Student): Maybe { - return sdk.getLuckyNumber(student.schoolShortName).map { + return sdk.init(student).getLuckyNumber(student.schoolShortName).map { LuckyNumber( studentId = student.studentId, date = LocalDate.now(), 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 index 386391b4f..53cf0a983 100644 --- 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 @@ -1,7 +1,10 @@ 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 io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED import io.reactivex.Maybe @@ -10,7 +13,10 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) { +class MessageLocal @Inject constructor( + private val messagesDb: MessagesDao, + private val messageAttachmentDao: MessageAttachmentDao +) { fun saveMessages(messages: List) { messagesDb.insertAll(messages) @@ -24,8 +30,12 @@ class MessageLocal @Inject constructor(private val messagesDb: MessagesDao) { messagesDb.deleteAll(messages) } - fun getMessage(id: Long): Single { - return messagesDb.load(id) + fun getMessageWithAttachment(student: Student, message: Message): Single { + return messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) + } + + fun saveMessageAttachments(attachments: List) { + messageAttachmentDao.insertAttachments(attachments) } fun getMessages(student: Student, folder: MessageFolder): Maybe> { 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 index d27eb904d..1808c048b 100644 --- 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 @@ -1,12 +1,14 @@ 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 io.reactivex.Single import org.threeten.bp.LocalDateTime.now import javax.inject.Inject @@ -17,7 +19,7 @@ import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient class MessageRemote @Inject constructor(private val sdk: Sdk) { fun getMessages(student: Student, semester: Semester, folder: MessageFolder): Single> { - return sdk.getMessages(Folder.valueOf(folder.name), semester.start.atStartOfDay(), semester.end.atStartOfDay()).map { messages -> + return sdk.init(student).getMessages(Folder.valueOf(folder.name), semester.start.atStartOfDay(), semester.end.atStartOfDay()).map { messages -> messages.map { Message( studentId = student.id.toInt(), @@ -33,18 +35,29 @@ class MessageRemote @Inject constructor(private val sdk: Sdk) { unread = it.unread ?: false, unreadBy = it.unreadBy ?: 0, readBy = it.readBy ?: 0, - removed = it.removed + removed = it.removed, + hasAttachments = it.hasAttachments ) } } } - fun getMessagesContent(message: Message, markAsRead: Boolean = false): Single { - return sdk.getMessageContent(message.messageId, message.folderId, markAsRead, message.realId) + fun getMessagesContentDetails(student: Student, message: Message, markAsRead: Boolean = false): Single>> { + return sdk.init(student).getMessageDetails(message.messageId, message.folderId, markAsRead, message.realId).map { details -> + details.content to details.attachments.map { + MessageAttachment( + realId = it.id, + messageId = it.messageId, + oneDriveId = it.oneDriveId, + url = it.url, + filename = it.filename + ) + } + } } - fun sendMessage(subject: String, content: String, recipients: List): Single { - return sdk.sendMessage( + fun sendMessage(student: Student, subject: String, content: String, recipients: List): Single { + return sdk.init(student).sendMessage( subject = subject, content = content, recipients = recipients.map { @@ -61,7 +74,7 @@ class MessageRemote @Inject constructor(private val sdk: Sdk) { ) } - fun deleteMessage(message: Message): Single { - return sdk.deleteMessages(listOf(Pair(message.realId, message.folderId))) + fun deleteMessage(student: Student, message: Message): Single { + return sdk.init(student).deleteMessages(listOf(Pair(message.realId, 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 index 3d860a047..2d2c0430c 100644 --- 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 @@ -2,8 +2,8 @@ package io.github.wulkanowy.data.repositories.message import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student @@ -11,7 +11,6 @@ import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED import io.github.wulkanowy.sdk.pojo.SentMessage import io.github.wulkanowy.utils.uniqueSubtract import io.reactivex.Completable -import io.reactivex.Maybe import io.reactivex.Single import timber.log.Timber import java.net.UnknownHostException @@ -22,59 +21,53 @@ import javax.inject.Singleton class MessageRepository @Inject constructor( private val settings: InternetObservingSettings, private val local: MessageLocal, - private val remote: MessageRemote, - private val sdkHelper: SdkHelper + private val remote: MessageRemote ) { fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single> { - return Single.just(sdkHelper.init(student)) - .flatMap { _ -> - local.getMessages(student, folder).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getMessages(student, semester, folder) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getMessages(student, folder).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteMessages(old.uniqueSubtract(new)) - local.saveMessages(new.uniqueSubtract(old) - .onEach { - it.isNotified = !notify - }) - } - }.flatMap { local.getMessages(student, folder).toSingle(emptyList()) } - ) - } + return local.getMessages(student, folder).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getMessages(student, semester, folder) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getMessages(student, folder).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteMessages(old.uniqueSubtract(new)) + local.saveMessages(new.uniqueSubtract(old) + .onEach { + it.isNotified = !notify + }) + } + }.flatMap { local.getMessages(student, folder).toSingle(emptyList()) } + ) } - fun getMessage(student: Student, messageDbId: Long, markAsRead: Boolean = false): Single { - return Single.just(sdkHelper.init(student)) - .flatMap { _ -> - local.getMessage(messageDbId) - .filter { - it.content.isNotEmpty().also { status -> - Timber.d("Message content in db empty: ${!status}") - } - } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) local.getMessage(messageDbId) - else Single.error(UnknownHostException()) - } - .flatMap { dbMessage -> - remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess { - local.updateMessages(listOf(dbMessage.copy(unread = !markAsRead).apply { - id = dbMessage.id - content = content.ifBlank { it } - })) - Timber.d("Message $messageDbId with blank content: ${dbMessage.content.isBlank()}, marked as read") - } - }.flatMap { - local.getMessage(messageDbId) - } - ) + fun getMessage(student: Student, message: Message, markAsRead: Boolean = false): Single { + return local.getMessageWithAttachment(student, message) + .filter { + it.message.content.isNotEmpty().also { status -> + Timber.d("Message content in db empty: ${!status}") + } && !it.message.unread } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) local.getMessageWithAttachment(student, message) + else Single.error(UnknownHostException()) + } + .flatMap { dbMessage -> + remote.getMessagesContentDetails(student, dbMessage.message, markAsRead).doOnSuccess { (downloadedMessage, attachments) -> + local.updateMessages(listOf(dbMessage.message.copy(unread = !markAsRead).apply { + id = dbMessage.message.id + content = content.ifBlank { downloadedMessage } + })) + local.saveMessageAttachments(attachments) + Timber.d("Message ${message.messageId} with blank content: ${dbMessage.message.content.isBlank()}, marked as read") + } + }.flatMap { + local.getMessageWithAttachment(student, message) + } + ) } fun getNotNotifiedMessages(student: Student): Single> { @@ -83,29 +76,24 @@ class MessageRepository @Inject constructor( .toSingle(emptyList()) } - fun updateMessage(message: Message): Completable { - return Completable.fromCallable { local.updateMessages(listOf(message)) } - } - fun updateMessages(messages: List): Completable { return Completable.fromCallable { local.updateMessages(messages) } } - fun sendMessage(subject: String, content: String, recipients: List): Single { + fun sendMessage(student: Student, subject: String, content: String, recipients: List): Single { return ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.sendMessage(subject, content, recipients) + if (it) remote.sendMessage(student, subject, content, recipients) else Single.error(UnknownHostException()) } } - fun deleteMessage(message: Message): Maybe { + fun deleteMessage(student: Student, message: Message): Single { return ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.deleteMessage(message) + if (it) remote.deleteMessage(student, message) else Single.error(UnknownHostException()) } - .filter { it } .doOnSuccess { if (!message.removed) local.updateMessages(listOf(message.copy(removed = true).apply { id = message.id 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 index c43c3f21e..800f95971 100644 --- 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 @@ -2,8 +2,10 @@ 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 io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -11,13 +13,14 @@ import javax.inject.Singleton @Singleton class MobileDeviceRemote @Inject constructor(private val sdk: Sdk) { - fun getDevices(semester: Semester): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getRegisteredDevices() + fun getDevices(student: Student, semester: Semester): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getRegisteredDevices() .map { devices -> devices.map { MobileDevice( studentId = semester.studentId, - date = it.date, + date = it.createDate, deviceId = it.id, name = it.name ) @@ -25,12 +28,14 @@ class MobileDeviceRemote @Inject constructor(private val sdk: Sdk) { } } - fun unregisterDevice(semester: Semester, device: MobileDevice): Single { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).unregisterDevice(device.deviceId) + fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice): Single { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .unregisterDevice(device.deviceId) } - fun getToken(semester: Semester): Single { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getToken() + fun getToken(student: Student, semester: Semester): Single { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getToken() .map { MobileDeviceToken( token = it.token, 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 index ac450ff46..545846e85 100644 --- 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 @@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.uniqueSubtract import io.reactivex.Single @@ -18,11 +19,11 @@ class MobileDeviceRepository @Inject constructor( private val remote: MobileDeviceRemote ) { - fun getDevices(semester: Semester, forceRefresh: Boolean = false): Single> { + fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean = false): Single> { return local.getDevices(semester).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getDevices(semester) + if (it) remote.getDevices(student, semester) else Single.error(UnknownHostException()) }.flatMap { new -> local.getDevices(semester).toSingle(emptyList()) @@ -34,18 +35,18 @@ class MobileDeviceRepository @Inject constructor( ).flatMap { local.getDevices(semester).toSingle(emptyList()) } } - fun unregisterDevice(semester: Semester, device: MobileDevice): Single { + fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice): Single { return ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.unregisterDevice(semester, device) + if (it) remote.unregisterDevice(student, semester, device) else Single.error(UnknownHostException()) } } - fun getToken(semester: Semester): Single { + fun getToken(student: Student, semester: Semester): Single { return ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getToken(semester) + if (it) remote.getToken(student, semester) else Single.error(UnknownHostException()) } } 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 index 503575b69..2c62b608d 100644 --- 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 @@ -2,7 +2,9 @@ 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 io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -10,15 +12,20 @@ import javax.inject.Singleton @Singleton class NoteRemote @Inject constructor(private val sdk: Sdk) { - fun getNotes(semester: Semester): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getNotes(semester.semesterId) + fun getNotes(student: Student, semester: Semester): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getNotes(semester.semesterId) .map { notes -> notes.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 index 52cb2d0f5..e155e2bad 100644 --- 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 @@ -23,7 +23,7 @@ class NoteRepository @Inject constructor( return local.getNotes(student).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getNotes(semester) + if (it) remote.getNotes(student, semester) else Single.error(UnknownHostException()) }.flatMap { new -> local.getNotes(student).toSingle(emptyList()) 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 index f070ea765..e5b16a156 100644 --- 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 @@ -3,7 +3,9 @@ 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 io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -12,15 +14,15 @@ import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient @Singleton class RecipientRemote @Inject constructor(private val sdk: Sdk) { - fun getRecipients(role: Int, unit: ReportingUnit): Single> { - return sdk.getRecipients(unit.realId, role) + fun getRecipients(student: Student, role: Int, unit: ReportingUnit): Single> { + return sdk.init(student).getRecipients(unit.realId, role) .map { recipients -> recipients.map { it.toRecipient() } } } - fun getMessageRecipients(message: Message): Single> { - return sdk.getMessageRecipients(message.messageId, message.senderId) + fun getMessageRecipients(student: Student, message: Message): Single> { + return sdk.init(student).getMessageRecipients(message.messageId, message.senderId) .map { recipients -> recipients.map { it.toRecipient() } } 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 index d29369b94..6f8a72af2 100644 --- 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 @@ -2,7 +2,6 @@ package io.github.wulkanowy.data.repositories.recipient import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.ReportingUnit @@ -17,36 +16,31 @@ import javax.inject.Singleton class RecipientRepository @Inject constructor( private val settings: InternetObservingSettings, private val local: RecipientLocal, - private val remote: RecipientRemote, - private val sdkHelper: SdkHelper + private val remote: RecipientRemote ) { fun getRecipients(student: Student, role: Int, unit: ReportingUnit, forceRefresh: Boolean = false): Single> { - return Single.just(sdkHelper.init(student)) - .flatMap { _ -> - local.getRecipients(student, role, unit).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getRecipients(role, unit) - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getRecipients(student, role, unit).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteRecipients(old.uniqueSubtract(new)) - local.saveRecipients(new.uniqueSubtract(old)) - } - }.flatMap { - local.getRecipients(student, role, unit).toSingle(emptyList()) + return local.getRecipients(student, role, unit).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getRecipients(student, role, unit) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getRecipients(student, role, unit).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteRecipients(old.uniqueSubtract(new)) + local.saveRecipients(new.uniqueSubtract(old)) } - ) - } + }.flatMap { + local.getRecipients(student, role, unit).toSingle(emptyList()) + } + ) } fun getMessageRecipients(student: Student, message: Message): Single> { - return Single.just(sdkHelper.init(student)) - .flatMap { ReactiveNetwork.checkInternetConnectivity(settings) } + return ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getMessageRecipients(message) + if (it) remote.getMessageRecipients(student, message) else Single.error(UnknownHostException()) } } 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 index 5dbabc545..1fd8b08e3 100644 --- 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 @@ -1,7 +1,9 @@ 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 io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -9,8 +11,8 @@ import javax.inject.Singleton @Singleton class ReportingUnitRemote @Inject constructor(private val sdk: Sdk) { - fun getReportingUnits(): Single> { - return sdk.getReportingUnits().map { + fun getReportingUnits(student: Student): Single> { + return sdk.init(student).getReportingUnits().map { it.map { unit -> ReportingUnit( studentId = sdk.studentId, 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 index 4c8370e68..ee5440984 100644 --- 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 @@ -2,7 +2,6 @@ package io.github.wulkanowy.data.repositories.reportingunit import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.utils.uniqueSubtract @@ -16,41 +15,34 @@ import javax.inject.Singleton class ReportingUnitRepository @Inject constructor( private val settings: InternetObservingSettings, private val local: ReportingUnitLocal, - private val remote: ReportingUnitRemote, - private val sdkHelper: SdkHelper + private val remote: ReportingUnitRemote ) { fun getReportingUnits(student: Student, forceRefresh: Boolean = false): Single> { - return Single.just(sdkHelper.init(student)) - .flatMap { _ -> - local.getReportingUnits(student).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getReportingUnits() - else Single.error(UnknownHostException()) - }.flatMap { new -> - local.getReportingUnits(student).toSingle(emptyList()) - .doOnSuccess { old -> - local.deleteReportingUnits(old.uniqueSubtract(new)) - local.saveReportingUnits(new.uniqueSubtract(old)) - } - }.flatMap { local.getReportingUnits(student).toSingle(emptyList()) } - ) - } + return local.getReportingUnits(student).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getReportingUnits(student) + else Single.error(UnknownHostException()) + }.flatMap { new -> + local.getReportingUnits(student).toSingle(emptyList()) + .doOnSuccess { old -> + local.deleteReportingUnits(old.uniqueSubtract(new)) + local.saveReportingUnits(new.uniqueSubtract(old)) + } + }.flatMap { local.getReportingUnits(student).toSingle(emptyList()) } + ) } fun getReportingUnit(student: Student, unitId: Int): Maybe { - return Maybe.just(sdkHelper.init(student)) - .flatMap { _ -> - local.getReportingUnit(student, unitId) - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) getReportingUnits(student, true) - else Single.error(UnknownHostException()) - }.flatMapMaybe { - local.getReportingUnit(student, unitId) - } - ) - } + return local.getReportingUnit(student, unitId) + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) getReportingUnits(student, true) + else Single.error(UnknownHostException()) + }.flatMapMaybe { + local.getReportingUnit(student, unitId) + } + ) } } 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 index 4d5c92a9e..6a95a446b 100644 --- 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 @@ -2,14 +2,17 @@ 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 io.reactivex.Single import javax.inject.Inject class SchoolRemote @Inject constructor(private val sdk: Sdk) { - fun getSchoolInfo(semester: Semester): Single { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getSchool() + fun getSchoolInfo(student: Student, semester: Semester): Single { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getSchool() .map { School( studentId = semester.studentId, 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 index a352324d2..1715a28d5 100644 --- 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 @@ -4,6 +4,7 @@ import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.reactivex.Maybe import io.reactivex.Single import java.net.UnknownHostException @@ -17,11 +18,11 @@ class SchoolRepository @Inject constructor( private val remote: SchoolRemote ) { - fun getSchoolInfo(semester: Semester, forceRefresh: Boolean = false): Maybe { + fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean = false): Maybe { return local.getSchool(semester).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getSchoolInfo(semester) + if (it) remote.getSchoolInfo(student, semester) else Single.error(UnknownHostException()) }.flatMapMaybe { new -> local.getSchool(semester) 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 index f7b98a4b7..90f0e1d71 100644 --- 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 @@ -3,6 +3,7 @@ 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 io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -11,7 +12,7 @@ import javax.inject.Singleton class SemesterRemote @Inject constructor(private val sdk: Sdk) { fun getSemesters(student: Student): Single> { - return sdk.getSemesters().map { semesters -> + return sdk.init(student).getSemesters().map { semesters -> semesters.map { Semester( studentId = student.studentId, 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/semester/SemesterRepository.kt index a13658209..68946beef 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt @@ -2,13 +2,12 @@ package io.github.wulkanowy.data.repositories.semester import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.SdkHelper 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.getCurrentOrLast import io.github.wulkanowy.utils.isCurrent import io.github.wulkanowy.utils.uniqueSubtract -import io.reactivex.Maybe import io.reactivex.Single import java.net.UnknownHostException import javax.inject.Inject @@ -18,31 +17,28 @@ import javax.inject.Singleton class SemesterRepository @Inject constructor( private val remote: SemesterRemote, private val local: SemesterLocal, - private val settings: InternetObservingSettings, - private val sdkHelper: SdkHelper + private val settings: InternetObservingSettings ) { fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false): Single> { - return Maybe.just(sdkHelper.init(student)) - .flatMap { - local.getSemesters(student).filter { !forceRefresh }.filter { - if (refreshOnNoCurrent) { - it.any { semester -> semester.isCurrent } - } else true - } + return local.getSemesters(student).filter { !forceRefresh }.filter { semesters -> + when { + Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> semesters.firstOrNull { it.isCurrent }?.diaryId != 0 + refreshOnNoCurrent -> semesters.any { semester -> semester.isCurrent } + else -> true } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - if (it) remote.getSemesters(student) - else Single.error(UnknownHostException()) - }.flatMap { new -> - if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!") + }.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMap { + if (it) remote.getSemesters(student) + else Single.error(UnknownHostException()) + }.flatMap { new -> + if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!") - local.getSemesters(student).toSingle(emptyList()).doOnSuccess { old -> - local.deleteSemesters(old.uniqueSubtract(new)) - local.saveSemesters(new.uniqueSubtract(old)) - } - }.flatMap { local.getSemesters(student).toSingle(emptyList()) }) + local.getSemesters(student).toSingle(emptyList()).doOnSuccess { old -> + local.deleteSemesters(old.uniqueSubtract(new)) + local.saveSemesters(new.uniqueSubtract(old)) + } + }.flatMap { local.getSemesters(student).toSingle(emptyList()) }) } fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Single { 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 index a3576ca22..06b9bb4ac 100644 --- 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 @@ -29,10 +29,18 @@ class StudentLocal @Inject constructor( fun getStudents(decryptPass: Boolean): Maybe> { return studentDb.loadAll() - .map { list -> list.map { it.apply { if (decryptPass) password = decrypt(password) } } } + .map { list -> list.map { it.apply { if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password) } } } .filter { it.isNotEmpty() } } + fun getStudentById(id: Int): Maybe { + return studentDb.loadById(id).map { + it.apply { + if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password) + } + } + } + fun getCurrentStudent(decryptPass: Boolean): Maybe { return studentDb.loadCurrent().map { it.apply { 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 index 270d080e1..bebd1eb9c 100644 --- 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 @@ -47,6 +47,12 @@ class StudentRepository @Inject constructor( return local.getStudents(decryptPass).toSingle(emptyList()) } + fun getStudentById(id: Int): Single { + return local.getStudentById(id) + .switchIfEmpty(Maybe.error(NoCurrentStudentException())) + .toSingle() + } + fun getCurrentStudent(decryptPass: Boolean = true): Single { return local.getCurrentStudent(decryptPass) .switchIfEmpty(Maybe.error(NoCurrentStudentException())) 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 index 63e334019..f8c13e6c2 100644 --- 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 @@ -12,7 +12,7 @@ class SubjectLocal @Inject constructor(private val subjectDao: SubjectDao) { fun getSubjects(semester: Semester): Maybe> { return subjectDao.loadAll(semester.diaryId, semester.studentId) - .filter { !it.isEmpty() } + .filter { it.isNotEmpty() } } fun saveSubjects(subjects: List) { 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 index 32dbb2f2e..d30232f82 100644 --- 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 @@ -1,8 +1,10 @@ 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 io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -10,8 +12,9 @@ import javax.inject.Singleton @Singleton class SubjectRemote @Inject constructor(private val sdk: Sdk) { - fun getSubjects(semester: Semester): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getSubjects() + fun getSubjects(student: Student, semester: Semester): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getSubjects() .map { subjects -> subjects.map { Subject( 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 index 0c5f386b6..649904da1 100644 --- 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 @@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.subject import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.utils.uniqueSubtract import io.reactivex.Single @@ -17,11 +18,11 @@ class SubjectRepository @Inject constructor( private val remote: SubjectRemote ) { - fun getSubjects(semester: Semester, forceRefresh: Boolean = false): Single> { + fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false): Single> { return local.getSubjects(semester).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getSubjects(semester) + if (it) remote.getSubjects(student, semester) else Single.error(UnknownHostException()) }.flatMap { new -> local.getSubjects(semester) 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 index d4401bfb2..01552f748 100644 --- 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 @@ -1,8 +1,10 @@ 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 io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @@ -10,8 +12,9 @@ import javax.inject.Singleton @Singleton class TeacherRemote @Inject constructor(private val sdk: Sdk) { - fun getTeachers(semester: Semester): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getTeachers(semester.semesterId) + fun getTeachers(student: Student, semester: Semester): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getTeachers(semester.semesterId) .map { teachers -> teachers.map { Teacher( 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 index 19e7a32b8..3c10be73f 100644 --- 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 @@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.teacher import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.utils.uniqueSubtract import io.reactivex.Single @@ -17,11 +18,11 @@ class TeacherRepository @Inject constructor( private val remote: TeacherRemote ) { - fun getTeachers(semester: Semester, forceRefresh: Boolean = false): Single> { + fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean = false): Single> { return local.getTeachers(semester).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getTeachers(semester) + if (it) remote.getTeachers(student, semester) else Single.error(UnknownHostException()) }.flatMap { new -> local.getTeachers(semester).toSingle(emptyList()) 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 index c0aa16ebb..22cb947db 100644 --- 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 @@ -1,8 +1,10 @@ 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 io.reactivex.Single import org.threeten.bp.LocalDate import javax.inject.Inject @@ -11,8 +13,9 @@ import javax.inject.Singleton @Singleton class TimetableRemote @Inject constructor(private val sdk: Sdk) { - fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return sdk.switchDiary(semester.diaryId, semester.schoolYear).getTimetable(startDate, endDate) + fun getTimetable(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { + return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) + .getTimetable(startDate, endDate) .map { lessons -> lessons.map { Timetable( 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 index 20e183c25..42812b30f 100644 --- 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 @@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.timetable import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings 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.utils.friday import io.github.wulkanowy.utils.monday @@ -20,11 +21,11 @@ class TimetableRepository @Inject constructor( private val remote: TimetableRemote ) { - fun getTimetable(semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> { + fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single> { return Single.fromCallable { start.monday to end.friday }.flatMap { (monday, friday) -> local.getTimetable(semester, monday, friday).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap { - if (it) remote.getTimetable(semester, monday, friday) + if (it) remote.getTimetable(student, semester, monday, friday) else Single.error(UnknownHostException()) }.flatMap { new -> local.getTimetable(semester, monday, friday) diff --git a/app/src/main/java/io/github/wulkanowy/di/BindingModule.kt b/app/src/main/java/io/github/wulkanowy/di/BindingModule.kt index 98061e609..ba8c78d3f 100644 --- a/app/src/main/java/io/github/wulkanowy/di/BindingModule.kt +++ b/app/src/main/java/io/github/wulkanowy/di/BindingModule.kt @@ -3,6 +3,7 @@ package io.github.wulkanowy.di import dagger.Module import dagger.android.ContributesAndroidInjector import io.github.wulkanowy.di.scopes.PerActivity +import io.github.wulkanowy.ui.base.ErrorDialog import io.github.wulkanowy.ui.modules.login.LoginActivity import io.github.wulkanowy.ui.modules.login.LoginModule import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity @@ -18,6 +19,9 @@ import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider @Module internal abstract class BindingModule { + @ContributesAndroidInjector + abstract fun bindErrorDialog(): ErrorDialog + @PerActivity @ContributesAndroidInjector abstract fun bindSplashActivity(): SplashActivity 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 0b1b357ac..7240a50bb 100644 --- a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt +++ b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt @@ -15,6 +15,7 @@ import io.github.wulkanowy.services.sync.channels.LuckyNumberChannel import io.github.wulkanowy.services.sync.channels.NewGradesChannel import io.github.wulkanowy.services.sync.channels.NewMessagesChannel import io.github.wulkanowy.services.sync.channels.NewNotesChannel +import io.github.wulkanowy.services.sync.channels.PushChannel import io.github.wulkanowy.services.sync.works.AttendanceSummaryWork import io.github.wulkanowy.services.sync.works.AttendanceWork import io.github.wulkanowy.services.sync.works.CompletedLessonWork @@ -126,4 +127,8 @@ abstract class ServicesModule { @Binds @IntoSet abstract fun provideNewNotesChannel(channel: NewNotesChannel): Channel + + @Binds + @IntoSet + abstract fun providePushChannel(channel: PushChannel): Channel } 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 3d0cbcf63..dda2abe0c 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 @@ -5,18 +5,24 @@ import android.os.Build.VERSION_CODES.O import androidx.core.app.NotificationManagerCompat import androidx.work.BackoffPolicy.EXPONENTIAL import androidx.work.Constraints +import androidx.work.Data import androidx.work.ExistingPeriodicWorkPolicy.KEEP import androidx.work.ExistingPeriodicWorkPolicy.REPLACE +import androidx.work.ExistingWorkPolicy import androidx.work.NetworkType.CONNECTED import androidx.work.NetworkType.UNMETERED +import androidx.work.OneTimeWorkRequestBuilder import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkInfo import androidx.work.WorkManager +import com.paulinasadowska.rxworkmanagerobservers.extensions.getWorkInfoByIdObservable 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.services.sync.channels.Channel import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.isHolidays +import io.reactivex.Observable import org.threeten.bp.LocalDate.now import timber.log.Timber import java.util.concurrent.TimeUnit.MINUTES @@ -42,13 +48,13 @@ class SyncManager @Inject constructor( } if (sharedPrefProvider.getLong(APP_VERSION_CODE_KEY, -1L) != appInfo.versionCode.toLong()) { - startSyncWorker(true) + startPeriodicSyncWorker(true) sharedPrefProvider.putLong(APP_VERSION_CODE_KEY, appInfo.versionCode.toLong(), true) } Timber.i("SyncManager was initialized") } - fun startSyncWorker(restart: Boolean = false) { + fun startPeriodicSyncWorker(restart: Boolean = false) { if (preferencesRepository.isServiceEnabled && !now().isHolidays) { workManager.enqueueUniquePeriodicWork(SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP, PeriodicWorkRequestBuilder(preferencesRepository.servicesInterval, MINUTES) @@ -61,6 +67,19 @@ class SyncManager @Inject constructor( } } + fun startOneTimeSyncWorker(): Observable { + val work = OneTimeWorkRequestBuilder() + .setInputData( + Data.Builder() + .putBoolean("one_time", true) + .build() + ) + .build() + + workManager.enqueueUniqueWork("${SyncWorker::class.java.simpleName}_one_time", ExistingWorkPolicy.REPLACE, work) + return workManager.getWorkInfoByIdObservable(work.id) + } + fun stopSyncWorker() { workManager.cancelUniqueWork(SyncWorker::class.java.simpleName) } 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 68702d9a9..126d856aa 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 @@ -5,6 +5,7 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat.BigTextStyle import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT import androidx.core.app.NotificationManagerCompat +import androidx.work.Data import androidx.work.ListenableWorker import androidx.work.RxWorker import androidx.work.WorkerParameters @@ -15,6 +16,7 @@ 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.sdk.exception.FeatureDisabledException +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException import io.github.wulkanowy.services.sync.channels.DebugChannel import io.github.wulkanowy.services.sync.works.Work import io.github.wulkanowy.utils.getCompatColor @@ -43,6 +45,10 @@ class SyncWorker @AssistedInject constructor( .flatMapCompletable { semester -> Completable.mergeDelayError(works.map { work -> work.create(student, semester) + .onErrorResumeNext { + if (it is FeatureDisabledException || it is FeatureNotAvailableException) Completable.complete() + else Completable.error(it) + } .doOnSubscribe { Timber.i("${work::class.java.simpleName} is starting") } .doOnError { Timber.i("${work::class.java.simpleName} result: An exception occurred") } .doOnComplete { Timber.i("${work::class.java.simpleName} result: Success") } @@ -52,8 +58,15 @@ class SyncWorker @AssistedInject constructor( .toSingleDefault(Result.success()) .onErrorReturn { Timber.e(it, "There was an error during synchronization") - if (it is FeatureDisabledException) Result.success() - else Result.retry() + when { + inputData.getBoolean("one_time", false) -> { + Result.failure(Data.Builder() + .putString("error", it.toString()) + .build() + ) + } + else -> Result.retry() + } } .doOnSuccess { if (preferencesRepository.isDebugNotificationEnable) notify(it) @@ -64,7 +77,7 @@ class SyncWorker @AssistedInject constructor( private fun notify(result: Result) { notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(applicationContext, DebugChannel.CHANNEL_ID) .setContentTitle("Debug notification") - .setSmallIcon(R.drawable.ic_more_settings) + .setSmallIcon(R.drawable.ic_stat_push) .setAutoCancel(true) .setColor(applicationContext.getCompatColor(R.color.colorPrimary)) .setStyle(BigTextStyle().bigText("${SyncWorker::class.java.simpleName} result: $result")) diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/PushChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/PushChannel.kt new file mode 100644 index 000000000..a0e24ab45 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/PushChannel.kt @@ -0,0 +1,32 @@ +package io.github.wulkanowy.services.sync.channels + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import androidx.core.app.NotificationManagerCompat +import io.github.wulkanowy.R +import javax.inject.Inject + +@TargetApi(26) +class PushChannel @Inject constructor( + private val notificationManager: NotificationManagerCompat, + private val context: Context +) : Channel { + + companion object { + const val CHANNEL_ID = "push_channel" + } + + override fun create() { + notificationManager.createNotificationChannel( + NotificationChannel(CHANNEL_ID, context.getString(R.string.channel_push), NotificationManager.IMPORTANCE_HIGH) + .apply { + enableLights(true) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + } + ) + } +} 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 01978c5b6..5f7d7efa2 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 @@ -11,7 +11,7 @@ class AttendanceSummaryWork @Inject constructor( ) : Work { override fun create(student: Student, semester: Semester): Completable { - return attendanceSummaryRepository.getAttendanceSummary(semester, -1, true).ignoreElement() + return attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, true).ignoreElement() } } 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 e4b55b0ea..e8579ddba 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 @@ -12,7 +12,7 @@ import javax.inject.Inject class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work { override fun create(student: Student, semester: Semester): Completable { - return attendanceRepository.getAttendance(semester, now().monday, now().friday, true) + return attendanceRepository.getAttendance(student, semester, now().monday, now().friday, true) .ignoreElement() } } 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 29642ad6f..0da597e0a 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 @@ -14,7 +14,7 @@ class CompletedLessonWork @Inject constructor( ) : Work { override fun create(student: Student, semester: Semester): Completable { - return completedLessonsRepository.getCompletedLessons(semester, now().monday, now().friday, true) + return completedLessonsRepository.getCompletedLessons(student, semester, now().monday, now().friday, true) .ignoreElement() } } 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 8744fcc79..c6110bbbf 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 @@ -12,6 +12,6 @@ import javax.inject.Inject class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work { override fun create(student: Student, semester: Semester): Completable { - return examRepository.getExams(semester, now().monday, now().friday, true).ignoreElement() + return examRepository.getExams(student, semester, now().monday, now().friday, true).ignoreElement() } } 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 1de39a95f..327c71740 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 @@ -9,7 +9,7 @@ import javax.inject.Inject class GradeStatisticsWork @Inject constructor(private val gradeStatisticsRepository: GradeStatisticsRepository) : Work { override fun create(student: Student, semester: Semester): Completable { - return gradeStatisticsRepository.getGradesStatistics(semester, "Wszystkie", false, true) + return gradeStatisticsRepository.getGradesStatistics(student, semester, "Wszystkie", false, true) .ignoreElement() } } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt index 6de0bc5b0..4c8e955d1 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeSummaryWork.kt @@ -9,6 +9,6 @@ import javax.inject.Inject class GradeSummaryWork @Inject constructor(private val gradeSummaryRepository: GradeSummaryRepository) : Work { override fun create(student: Student, semester: Semester): Completable { - return gradeSummaryRepository.getGradesSummary(semester, true).ignoreElement() + return gradeSummaryRepository.getGradesSummary(student, semester, true).ignoreElement() } } 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 32b356c68..cf3484394 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 @@ -12,6 +12,6 @@ import javax.inject.Inject class HomeworkWork @Inject constructor(private val homeworkRepository: HomeworkRepository) : Work { override fun create(student: Student, semester: Semester): Completable { - return homeworkRepository.getHomework(semester, now().monday, now().friday, true).ignoreElement() + return homeworkRepository.getHomework(student, semester, now().monday, now().friday, true).ignoreElement() } } 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 18178d2d1..5a7a41d8d 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 @@ -9,6 +9,6 @@ import javax.inject.Inject class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work { override fun create(student: Student, semester: Semester): Completable { - return teacherRepository.getTeachers(semester, true).ignoreElement() + return teacherRepository.getTeachers(student, semester, true).ignoreElement() } } 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 743ae0e83..0990ed67a 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 @@ -12,7 +12,7 @@ import javax.inject.Inject class TimetableWork @Inject constructor(private val timetableRepository: TimetableRepository) : Work { override fun create(student: Student, semester: Semester): Completable { - return timetableRepository.getTimetable(semester, now().monday, now().friday, true) + return timetableRepository.getTimetable(student, semester, now().monday, now().friday, true) .ignoreElement() } } 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 3255ea683..ae3604bfd 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 @@ -6,19 +6,33 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.HorizontalScrollView import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.core.content.getSystemService -import androidx.fragment.app.DialogFragment import io.github.wulkanowy.R +import io.github.wulkanowy.sdk.exception.FeatureDisabledException +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException +import io.github.wulkanowy.sdk.exception.ServiceUnavailableException +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.getString +import io.github.wulkanowy.utils.openEmailClient +import io.github.wulkanowy.utils.openInternetBrowser import kotlinx.android.synthetic.main.dialog_error.* +import java.io.InterruptedIOException import java.io.PrintWriter import java.io.StringWriter +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import javax.inject.Inject -class ErrorDialog : DialogFragment() { +class ErrorDialog : BaseDialogFragment() { private lateinit var error: Throwable + @Inject + lateinit var appInfo: AppInfo + companion object { private const val ARGUMENT_KEY = "Data" @@ -49,6 +63,9 @@ class ErrorDialog : DialogFragment() { } errorDialogContent.text = stringWriter.toString() + with(errorDialogHorizontalScroll) { + post { fullScroll(HorizontalScrollView.FOCUS_LEFT) } + } errorDialogCopy.setOnClickListener { val clip = ClipData.newPlainText("wulkanowy", stringWriter.toString()) activity?.getSystemService()?.setPrimaryClip(clip) @@ -56,6 +73,30 @@ class ErrorDialog : DialogFragment() { Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show() } errorDialogCancel.setOnClickListener { dismiss() } + errorDialogReport.setOnClickListener { openEmailClient(stringWriter.toString()) } + errorDialogMessage.text = resources.getString(error) + errorDialogReport.isEnabled = when (error) { + is UnknownHostException, + is InterruptedIOException, + is SocketTimeoutException, + is ServiceUnavailableException, + is FeatureDisabledException, + is FeatureNotAvailableException -> false + else -> true + } + } + + private fun openEmailClient(content: String) { + requireContext().openEmailClient( + chooserTitle = getString(R.string.about_feedback), + email = "wulkanowyinc@gmail.com", + subject = "Zgłoszenie błędu", + body = requireContext().getString(R.string.about_feedback_template, + "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), appInfo.versionName + ) + "\n" + content, + onActivityNotFound = { + requireContext().openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage) + } + ) } } - 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 ba10af6bf..f746b36f8 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,21 +1,15 @@ package io.github.wulkanowy.ui.base import android.content.res.Resources -import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.R +import com.chuckerteam.chucker.api.ChuckerCollector import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.sdk.exception.BadCredentialsException -import io.github.wulkanowy.sdk.exception.FeatureDisabledException -import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException -import io.github.wulkanowy.sdk.exception.NotLoggedInException -import io.github.wulkanowy.sdk.exception.ServiceUnavailableException +import io.github.wulkanowy.utils.getString import io.github.wulkanowy.utils.security.ScramblerException import timber.log.Timber -import java.net.SocketTimeoutException -import java.net.UnknownHostException import javax.inject.Inject -open class ErrorHandler @Inject constructor(protected val resources: Resources, private val chuckCollector: ChuckCollector) { +open class ErrorHandler @Inject constructor(protected val resources: Resources, private val chuckerCollector: ChuckerCollector) { var showErrorMessage: (String, Throwable) -> Unit = { _, _ -> } @@ -24,24 +18,16 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources, var onNoCurrentStudent: () -> Unit = {} fun dispatch(error: Throwable) { - chuckCollector.onError(error.javaClass.simpleName, error) + chuckerCollector.onError(error.javaClass.simpleName, error) Timber.e(error, "An exception occurred while the Wulkanowy was running") proceed(error) } protected open fun proceed(error: Throwable) { - resources.run { - when (error) { - is UnknownHostException -> showErrorMessage(getString(R.string.error_no_internet), error) - is SocketTimeoutException -> showErrorMessage(getString(R.string.error_timeout), error) - is NotLoggedInException -> showErrorMessage(getString(R.string.error_login_failed), error) - is ServiceUnavailableException -> showErrorMessage(getString(R.string.error_service_unavailable), error) - is FeatureDisabledException -> showErrorMessage(getString(R.string.error_feature_disabled), error) - is ScramblerException, is BadCredentialsException -> onSessionExpired() - is NoCurrentStudentException -> onNoCurrentStudent() - is FeatureNotAvailableException -> showErrorMessage(getString(R.string.error_feature_not_available), error) - else -> showErrorMessage(getString(R.string.error_unknown), error) - } + when (error) { + is ScramblerException, is BadCredentialsException -> onSessionExpired() + is NoCurrentStudentException -> onNoCurrentStudent() + else -> showErrorMessage(resources.getString(error), error) } } 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 cb455d0a0..5a32ac837 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 @@ -1,12 +1,6 @@ package io.github.wulkanowy.ui.modules.about -import android.content.Intent -import android.content.Intent.ACTION_SENDTO -import android.content.Intent.EXTRA_EMAIL -import android.content.Intent.EXTRA_SUBJECT -import android.content.Intent.EXTRA_TEXT import android.graphics.drawable.Drawable -import android.net.Uri import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -23,6 +17,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.getCompatDrawable +import io.github.wulkanowy.utils.openEmailClient import io.github.wulkanowy.utils.openInternetBrowser import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_about.* @@ -124,26 +119,17 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { } override fun openEmailClient() { - val intent = Intent(ACTION_SENDTO) - .apply { - data = Uri.parse("mailto:") - putExtra(EXTRA_EMAIL, arrayOf("wulkanowyinc@gmail.com")) - putExtra(EXTRA_SUBJECT, "Zgłoszenie błędu") - putExtra(EXTRA_TEXT, "Tu umieść treść zgłoszenia\n\n${"-".repeat(40)}\n " + - """ - Build: ${appInfo.versionCode} - SDK: ${appInfo.systemVersion} - Device: ${appInfo.systemManufacturer} ${appInfo.systemModel} - """.trimIndent()) + requireContext().openEmailClient( + chooserTitle = getString(R.string.about_feedback), + email = "wulkanowyinc@gmail.com", + subject = "Zgłoszenie błędu", + body = requireContext().getString(R.string.about_feedback_template, + "${appInfo.systemManufacturer} ${appInfo.systemModel}", appInfo.systemVersion.toString(), appInfo.versionName + ), + onActivityNotFound = { + requireContext().openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage) } - - context?.let { - if (intent.resolveActivity(it.packageManager) != null) { - startActivity(Intent.createChooser(intent, getString(R.string.about_feedback))) - } else { - it.openInternetBrowser("https://github.com/wulkanowy/wulkanowy/issues", ::showMessage) - } - } + ) } override fun openFaqPage() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseItem.kt index 98c86e75a..8dcb89224 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseItem.kt @@ -19,7 +19,7 @@ class LicenseItem(val library: Library) : AbstractFlexibleItem>, holder: ViewHolder, position: Int, payloads: MutableList) { with(holder) { licenseItemName.text = library.libraryName - licenseItemSummary.text = library.license?.licenseName + licenseItemSummary.text = library.license?.licenseName?.takeIf { it.isNotBlank() } ?: library.libraryVersion } } 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 b5b8fcd13..dc48b098b 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 @@ -27,10 +27,6 @@ class LicensePresenter @Inject constructor( private fun loadData() { disposable.add(Single.fromCallable { view?.appLibraries } - .map { - val exclude = listOf("Android-Iconics", "CircleImageView", "FastAdapter", "Jsoup", "okio", "Retrofit") - it.filter { library -> !exclude.contains(library.libraryName) } - } .map { it.map { library -> LicenseItem(library) } } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) 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 679d5367d..7fc044744 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 @@ -188,13 +188,16 @@ class AttendancePresenter @Inject constructor( disposable.apply { clear() add(studentRepository.getCurrentStudent() - .delay(200, MILLISECONDS) - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { attendanceRepository.getAttendance(it, date, date, forceRefresh) } + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + attendanceRepository.getAttendance(student, semester, date, date, forceRefresh) + } + } .map { list -> if (prefRepository.isShowPresent) list else list.filter { !it.presence } } + .delay(200, MILLISECONDS) .map { items -> items.map { AttendanceItem(it) } } .map { items -> items.sortedBy { it.attendance.number } } .subscribeOn(schedulers.backgroundThread) @@ -228,9 +231,12 @@ class AttendancePresenter @Inject constructor( Timber.i("Excusing absence started") disposable.apply { add(studentRepository.getCurrentStudent() + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + attendanceRepository.excuseForAbsence(student, semester, toExcuseList, reason) + } + } .delay(200, MILLISECONDS) - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { attendanceRepository.excuseForAbsence(it, toExcuseList, reason) } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doOnSubscribe { 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 18f67726a..8fc5b6e4a 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 @@ -83,10 +83,13 @@ class AttendanceSummaryPresenter @Inject constructor( disposable.apply { clear() add(studentRepository.getCurrentStudent() - .delay(200, MILLISECONDS) - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { attendanceSummaryRepository.getAttendanceSummary(it, subjectId, forceRefresh) } + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { + attendanceSummaryRepository.getAttendanceSummary(student, it, subjectId, forceRefresh) + } + } .map { createAttendanceSummaryItems(it) to AttendanceSummaryScrollableHeader(formatPercentage(it.calculatePercentage())) } + .delay(200, MILLISECONDS) .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doFinally { @@ -126,8 +129,11 @@ class AttendanceSummaryPresenter @Inject constructor( private fun loadSubjects() { Timber.i("Loading attendance summary subjects started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { subjectRepository.getSubjects(it) } + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + subjectRepository.getSubjects(student, semester) + } + } .doOnSuccess { subjects = it } .map { ArrayList(it.map { subject -> subject.name }) } .subscribeOn(schedulers.backgroundThread) 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 35cf5b945..aac9bc4bb 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 @@ -112,9 +112,12 @@ class ExamPresenter @Inject constructor( disposable.apply { clear() add(studentRepository.getCurrentStudent() + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + examRepository.getExams(student, semester, currentDate.monday, currentDate.friday, forceRefresh) + } + } .delay(200, MILLISECONDS) - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { examRepository.getExams(it, currentDate.monday, currentDate.friday, forceRefresh) } .map { it.groupBy { exam -> exam.date }.toSortedMap() } .map { createExamItems(it) } .subscribeOn(schedulers.backgroundThread) 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 37bba4d43..3bb084d3a 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 @@ -34,7 +34,7 @@ class GradeAverageProvider @Inject constructor( val selectedSemester = semesters.single { it.semesterId == semesterId } val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 } - return getAverageFromGradeSummary(selectedSemester, forceRefresh) + return getAverageFromGradeSummary(student, selectedSemester, forceRefresh) .switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh) .flatMap { firstGrades -> if (selectedSemester == firstSemester) Single.just(firstGrades) @@ -52,7 +52,7 @@ class GradeAverageProvider @Inject constructor( private fun getOnlyOneSemesterAverage(student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean): Single>> { val selectedSemester = semesters.single { it.semesterId == semesterId } - return getAverageFromGradeSummary(selectedSemester, forceRefresh) + return getAverageFromGradeSummary(student, selectedSemester, forceRefresh) .switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh) .map { grades -> grades.map { if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) else it } @@ -61,8 +61,8 @@ class GradeAverageProvider @Inject constructor( }) } - private fun getAverageFromGradeSummary(selectedSemester: Semester, forceRefresh: Boolean): Maybe>> { - return gradeSummaryRepository.getGradesSummary(selectedSemester, forceRefresh) + private fun getAverageFromGradeSummary(student: Student, selectedSemester: Semester, forceRefresh: Boolean): Maybe>> { + return gradeSummaryRepository.getGradesSummary(student, selectedSemester, forceRefresh) .toMaybe() .flatMap { if (it.any { summary -> summary.average != .0 }) { 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 4ec0fafe0..90c4e38ef 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 @@ -48,7 +48,6 @@ class GradeStatisticsPresenter @Inject constructor( loadDataByType(semesterId, currentSubjectName, currentType, forceRefresh) } - fun onParentViewReselected() { view?.run { if (!isViewEmpty) resetView() @@ -118,8 +117,11 @@ class GradeStatisticsPresenter @Inject constructor( private fun loadSubjects() { Timber.i("Loading grade stats subjects started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { subjectRepository.getSubjects(it) } + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + subjectRepository.getSubjects(student, semester) + } + } .doOnSuccess { subjects = it } .map { ArrayList(it.map { subject -> subject.name }) } .subscribeOn(schedulers.backgroundThread) @@ -131,7 +133,7 @@ class GradeStatisticsPresenter @Inject constructor( showSubjects(true) } }, { - Timber.e("Loading grade stats subjects result: An exception occurred") + Timber.i("Loading grade stats subjects result: An exception occurred") errorHandler.dispatch(it) }) ) @@ -146,14 +148,15 @@ class GradeStatisticsPresenter @Inject constructor( private fun loadData(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean) { Timber.i("Loading grade stats data started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getSemesters(it) } - .flatMap { - val semester = it.first { item -> item.semesterId == semesterId } + .flatMap { student -> + semesterRepository.getSemesters(student).flatMap { semesters -> + val semester = semesters.first { item -> item.semesterId == semesterId } - when (type) { - ViewType.SEMESTER -> gradeStatisticsRepository.getGradesStatistics(semester, subjectName, true, forceRefresh) - ViewType.PARTIAL -> gradeStatisticsRepository.getGradesStatistics(semester, subjectName, false, forceRefresh) - ViewType.POINTS -> gradeStatisticsRepository.getGradesPointsStatistics(semester, subjectName, forceRefresh) + when (type) { + ViewType.SEMESTER -> gradeStatisticsRepository.getGradesStatistics(student, semester, subjectName, true, forceRefresh) + ViewType.PARTIAL -> gradeStatisticsRepository.getGradesStatistics(student, semester, subjectName, false, forceRefresh) + ViewType.POINTS -> gradeStatisticsRepository.getGradesPointsStatistics(student, semester, subjectName, forceRefresh) + } } } .subscribeOn(schedulers.backgroundThread) @@ -176,7 +179,7 @@ class GradeStatisticsPresenter @Inject constructor( } analytics.logEvent("load_grade_statistics", "items" to it.size, "force_refresh" to forceRefresh) }) { - Timber.e("Loading grade stats result: An exception occurred") + Timber.i("Loading grade stats result: An exception occurred") errorHandler.dispatch(it) }) } 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 5f4412773..c12f2a516 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 @@ -38,7 +38,7 @@ class GradeSummaryPresenter @Inject constructor( disposable.add(studentRepository.getCurrentStudent() .flatMap { semesterRepository.getSemesters(it).map { semesters -> it to semesters } } .flatMap { (student, semesters) -> - gradeSummaryRepository.getGradesSummary(semesters.first { it.semesterId == semesterId }, forceRefresh) + gradeSummaryRepository.getGradesSummary(student, semesters.first { it.semesterId == semesterId }, forceRefresh) .map { it.sortedBy { subject -> subject.subject } } .flatMap { gradesSummary -> averageProvider.getGradeAverage(student, semesters, semesterId, forceRefresh) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt deleted file mode 100644 index 427841886..000000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkDialog.kt +++ /dev/null @@ -1,49 +0,0 @@ -package io.github.wulkanowy.ui.modules.homework - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.DialogFragment -import io.github.wulkanowy.R -import io.github.wulkanowy.data.db.entities.Homework -import io.github.wulkanowy.utils.toFormattedString -import kotlinx.android.synthetic.main.dialog_homework.* - -class HomeworkDialog : DialogFragment() { - - private lateinit var homework: Homework - - companion object { - private const val ARGUMENT_KEY = "Item" - - fun newInstance(homework: Homework): HomeworkDialog { - return HomeworkDialog().apply { - arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) } - } - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setStyle(STYLE_NO_TITLE, 0) - arguments?.run { - homework = getSerializable(HomeworkDialog.ARGUMENT_KEY) as Homework - } - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.dialog_homework, container, false) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - homeworkDialogDate.text = homework.date.toFormattedString() - homeworkDialogEntryDate.text = homework.entryDate.toFormattedString() - homeworkDialogSubject.text = homework.subject - homeworkDialogTeacher.text = homework.teacher - homeworkDialogContent.text = homework.content - homeworkDialogClose.setOnClickListener { dismiss() } - } -} 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 3f8f1359a..a011a015c 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 @@ -13,6 +13,7 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.homework.details.HomeworkDetailsDialog import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.dpToPx @@ -73,6 +74,10 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView { homeworkAdapter.updateDataSet(data, true) } + fun onReloadList() { + presenter.reloadData() + } + override fun clearData() { homeworkAdapter.clear() } @@ -118,7 +123,7 @@ class HomeworkFragment : BaseFragment(), HomeworkView, MainView.TitledView { } override fun showTimetableDialog(homework: Homework) { - (activity as? MainActivity)?.showDialogFragment(HomeworkDialog.newInstance(homework)) + (activity as? MainActivity)?.showDialogFragment(HomeworkDetailsDialog.newInstance(homework)) } override fun onSaveInstanceState(outState: Bundle) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt index 2de9233f2..3c2dd7baf 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkItem.kt @@ -24,6 +24,8 @@ class HomeworkItem(header: HomeworkHeader, val homework: Homework) : homeworkItemSubject.text = homework.subject homeworkItemTeacher.text = homework.teacher homeworkItemContent.text = homework.content + homeworkItemCheckImage.visibility = if (homework.isDone) View.VISIBLE else View.GONE + homeworkItemAttachmentImage.visibility = if (!homework.isDone && homework.attachments.isNotEmpty()) View.VISIBLE else View.GONE } } @@ -43,7 +45,8 @@ class HomeworkItem(header: HomeworkHeader, val homework: Homework) : return result } - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : + FlexibleViewHolder(view, adapter), LayoutContainer { override val containerView: View get() = contentView } 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 7e1da314b..aae18df32 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 @@ -95,15 +95,22 @@ class HomeworkPresenter @Inject constructor( }) } + fun reloadData() { + loadData(currentDate, false) + } + private fun loadData(date: LocalDate, forceRefresh: Boolean = false) { Timber.i("Loading homework data started") currentDate = date disposable.apply { clear() add(studentRepository.getCurrentStudent() + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + homeworkRepository.getHomework(student, semester, currentDate, currentDate, forceRefresh) + } + } .delay(200, TimeUnit.MILLISECONDS) - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { homeworkRepository.getHomework(it, currentDate, currentDate, forceRefresh) } .map { it.groupBy { homework -> homework.date }.toSortedMap() } .map { createHomeworkItem(it) } .subscribeOn(schedulers.backgroundThread) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt new file mode 100644 index 000000000..3706b56e1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsAdapter.kt @@ -0,0 +1,84 @@ +package io.github.wulkanowy.ui.modules.homework.details + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.android.synthetic.main.item_homework_dialog_attachment.view.* +import kotlinx.android.synthetic.main.item_homework_dialog_details.view.* +import javax.inject.Inject + +class HomeworkDetailsAdapter @Inject constructor() : + RecyclerView.Adapter() { + + private enum class ViewType(val id: Int) { + DETAILS(1), + ATTACHMENTS_HEADER(2), + ATTACHMENT(3) + } + + private var attachments = emptyList>() + + var homework: Homework? = null + set(value) { + field = value + attachments = value?.attachments.orEmpty() + } + + var onAttachmentClickListener: (url: String) -> Unit = {} + + override fun getItemCount() = 1 + if (attachments.isNotEmpty()) attachments.size + 1 else 0 + + override fun getItemViewType(position: Int) = when (position) { + 0 -> ViewType.DETAILS.id + 1 -> ViewType.ATTACHMENTS_HEADER.id + else -> ViewType.ATTACHMENT.id + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + ViewType.ATTACHMENTS_HEADER.id -> AttachmentsHeaderViewHolder(inflater.inflate(R.layout.item_homework_dialog_attachments_header, parent, false)) + ViewType.ATTACHMENT.id -> AttachmentViewHolder(inflater.inflate(R.layout.item_homework_dialog_attachment, parent, false)) + else -> DetailsViewHolder(inflater.inflate(R.layout.item_homework_dialog_details, parent, false)) + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is DetailsViewHolder -> bindDetailsViewHolder(holder) + is AttachmentViewHolder -> bindAttachmentViewHolder(holder, position - 2) + } + } + + private fun bindDetailsViewHolder(holder: DetailsViewHolder) { + with(holder.view) { + homeworkDialogDate.text = homework?.date?.toFormattedString() + homeworkDialogEntryDate.text = homework?.entryDate?.toFormattedString() + homeworkDialogSubject.text = homework?.subject + homeworkDialogTeacher.text = homework?.teacher + homeworkDialogContent.text = homework?.content + } + } + + private fun bindAttachmentViewHolder(holder: AttachmentViewHolder, position: Int) { + val item = attachments[position] + + with(holder.view) { + homeworkDialogAttachment.text = item.second + setOnClickListener { + onAttachmentClickListener(item.first) + } + } + } + + class DetailsViewHolder(val view: View) : RecyclerView.ViewHolder(view) + + class AttachmentsHeaderViewHolder(val view: View) : RecyclerView.ViewHolder(view) + + class AttachmentViewHolder(val view: View) : RecyclerView.ViewHolder(view) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt new file mode 100644 index 000000000..54cb5c68c --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt @@ -0,0 +1,78 @@ +package io.github.wulkanowy.ui.modules.homework.details + +import android.annotation.SuppressLint +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.ui.base.BaseDialogFragment +import io.github.wulkanowy.ui.modules.homework.HomeworkFragment +import io.github.wulkanowy.utils.openInternetBrowser +import kotlinx.android.synthetic.main.dialog_homework.* +import javax.inject.Inject + +class HomeworkDetailsDialog : BaseDialogFragment(), HomeworkDetailsView { + + @Inject + lateinit var presenter: HomeworkDetailsPresenter + + @Inject + lateinit var detailsAdapter: HomeworkDetailsAdapter + + private lateinit var homework: Homework + + companion object { + private const val ARGUMENT_KEY = "Item" + + fun newInstance(homework: Homework): HomeworkDetailsDialog { + return HomeworkDetailsDialog().apply { + arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, homework) } + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, 0) + arguments?.run { + homework = getSerializable(ARGUMENT_KEY) as Homework + } + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.dialog_homework, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + @SuppressLint("SetTextI18n") + override fun initView() { + homeworkDialogRead.text = view?.context?.getString(if (homework.isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done) + homeworkDialogRead.setOnClickListener { presenter.toggleDone(homework) } + homeworkDialogClose.setOnClickListener { dismiss() } + + with(homeworkDialogRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = detailsAdapter.apply { + onAttachmentClickListener = { context.openInternetBrowser(it, ::showMessage) } + homework = this@HomeworkDetailsDialog.homework + } + } + } + + override fun updateMarkAsDoneLabel(isDone: Boolean) { + (parentFragment as? HomeworkFragment)?.onReloadList() + homeworkDialogRead.text = view?.context?.getString(if (isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done) + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} 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 new file mode 100644 index 000000000..04a97b8cd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt @@ -0,0 +1,44 @@ +package io.github.wulkanowy.ui.modules.homework.details + +import io.github.wulkanowy.data.db.entities.Homework +import io.github.wulkanowy.data.repositories.homework.HomeworkRepository +import io.github.wulkanowy.data.repositories.student.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.SchedulersProvider +import timber.log.Timber +import javax.inject.Inject + +class HomeworkDetailsPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val homeworkRepository: HomeworkRepository, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: HomeworkDetailsView) { + super.onAttachView(view) + view.initView() + Timber.i("Homework details view was initialized") + } + + fun toggleDone(homework: Homework) { + Timber.i("Homework details update start") + disposable.add(homeworkRepository.toggleDone(homework) + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + Timber.i("Homework details update: Success") + view?.run { + updateMarkAsDoneLabel(homework.isDone) + } + analytics.logEvent("homework_mark_as_done") + }) { + Timber.i("Homework details update result: An exception occurred") + errorHandler.dispatch(it) + } + ) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsView.kt new file mode 100644 index 000000000..697f22335 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsView.kt @@ -0,0 +1,10 @@ +package io.github.wulkanowy.ui.modules.homework.details + +import io.github.wulkanowy.ui.base.BaseView + +interface HomeworkDetailsView : BaseView { + + fun initView() + + fun updateMarkAsDoneLabel(isDone: Boolean) +} 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 090fe6de9..5ed181bf1 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,7 @@ package io.github.wulkanowy.ui.modules.login import android.content.res.Resources import android.database.sqlite.SQLiteConstraintException -import com.readystatesoftware.chuck.api.ChuckCollector +import com.chuckerteam.chucker.api.ChuckerCollector import io.github.wulkanowy.R import io.github.wulkanowy.sdk.exception.BadCredentialsException import io.github.wulkanowy.sdk.mobile.exception.InvalidPinException @@ -14,8 +14,8 @@ import javax.inject.Inject class LoginErrorHandler @Inject constructor( resources: Resources, - chuckCollector: ChuckCollector -) : ErrorHandler(resources, chuckCollector) { + chuckerCollector: ChuckerCollector +) : ErrorHandler(resources, chuckerCollector) { var onBadCredentials: () -> Unit = {} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt index 99090902b..aaea31eca 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt @@ -146,6 +146,20 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { } } + override fun setErrorLoginRequired() { + with(loginFormUsernameLayout) { + requestFocus() + error = getString(R.string.login_invalid_login) + } + } + + override fun setErrorEmailRequired() { + with(loginFormUsernameLayout) { + requestFocus() + error = getString(R.string.login_invalid_email) + } + } + override fun setErrorPassRequired(focus: Boolean) { with(loginFormPassLayout) { if (focus) requestFocus() 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 212f7b679..3a0d4a0d8 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 @@ -173,6 +173,8 @@ class LoginAdvancedPresenter @Inject constructor( val login = view?.formUsernameValue.orEmpty() val password = view?.formPassValue.orEmpty() + val host = view?.formHostValue.orEmpty() + val pin = view?.formPinValue.orEmpty() val symbol = view?.formSymbolValue.orEmpty() val token = view?.formTokenValue.orEmpty() @@ -196,26 +198,20 @@ class LoginAdvancedPresenter @Inject constructor( isCorrect = false } } - Sdk.Mode.SCRAPPER -> { + Sdk.Mode.HYBRID, Sdk.Mode.SCRAPPER -> { if (login.isEmpty()) { view?.setErrorUsernameRequired() isCorrect = false - } + } else { + if ("@" in login && "standard" !in host) { + view?.setErrorLoginRequired() + isCorrect = false + } - if (password.isEmpty()) { - view?.setErrorPassRequired(focus = isCorrect) - isCorrect = false - } - - if (password.length < 6 && password.isNotEmpty()) { - view?.setErrorPassInvalid(focus = isCorrect) - isCorrect = false - } - } - Sdk.Mode.HYBRID -> { - if (login.isEmpty()) { - view?.setErrorUsernameRequired() - isCorrect = false + if ("@" !in login && "standard" in host) { + view?.setErrorEmailRequired() + isCorrect = false + } } if (password.isEmpty()) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt index eb280235b..36c9a93f8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt @@ -41,6 +41,10 @@ interface LoginAdvancedView : BaseView { fun setErrorUsernameRequired() + fun setErrorLoginRequired() + + fun setErrorEmailRequired() + fun setErrorPassRequired(focus: Boolean) fun setErrorPassInvalid(focus: Boolean) 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 67e8a740a..058e702e6 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 @@ -7,7 +7,6 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup -import android.widget.ArrayAdapter import androidx.core.widget.doOnTextChanged import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Student @@ -46,9 +45,6 @@ class LoginFormFragment : BaseFragment(), LoginFormView { override val formHostSymbol: String get() = hostSymbols.getOrNull(hostKeys.indexOf(loginFormHost.text.toString())).orEmpty() - override val formSymbolValue: String - get() = loginFormSymbol.text.toString() - override val nicknameLabel: String get() = getString(R.string.login_nickname_hint) @@ -77,7 +73,6 @@ class LoginFormFragment : BaseFragment(), LoginFormView { loginFormUsername.doOnTextChanged { _, _, _, _ -> presenter.onUsernameTextChanged() } loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() } - loginFormSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() } loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() } loginFormSignIn.setOnClickListener { presenter.onSignInClick() } loginFormAdvancedButton.setOnClickListener { presenter.onAdvancedLoginClick() } @@ -86,9 +81,6 @@ class LoginFormFragment : BaseFragment(), LoginFormView { loginFormContactEmail.setOnClickListener { presenter.onEmailClick() } loginFormRecoverLink.setOnClickListener { presenter.onRecoverClick() } loginFormPass.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() } - loginFormSymbol.setOnEditorDoneSignIn { loginFormSignIn.callOnClick() } - - loginFormSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values))) with(loginFormHost) { setText(hostKeys.getOrNull(0).orEmpty()) @@ -102,18 +94,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView { loginFormPass.setText(pass) } - override fun setSymbol(symbol: String) { - loginFormSymbol.setText(symbol) - } - override fun setUsernameLabel(label: String) { loginFormUsernameLayout.hint = label } - override fun showSymbol(show: Boolean) { - loginFormSymbolLayout.visibility = if (show) VISIBLE else GONE - } - override fun setErrorUsernameRequired() { with(loginFormUsernameLayout) { requestFocus() @@ -121,10 +105,17 @@ class LoginFormFragment : BaseFragment(), LoginFormView { } } - override fun setErrorSymbolRequired(focus: Boolean) { - with(loginFormSymbolLayout) { - if (focus) requestFocus() - error = getString(R.string.login_symbol_helper) + override fun setErrorLoginRequired() { + with(loginFormUsernameLayout) { + requestFocus() + error = getString(R.string.login_invalid_login) + } + } + + override fun setErrorEmailRequired() { + with(loginFormUsernameLayout) { + requestFocus() + error = getString(R.string.login_invalid_email) } } @@ -157,10 +148,6 @@ class LoginFormFragment : BaseFragment(), LoginFormView { loginFormPassLayout.error = null } - override fun clearSymbolError() { - loginFormSymbolLayout.error = null - } - override fun showSoftKeyboard() { activity?.showSoftInput() } @@ -213,18 +200,21 @@ class LoginFormFragment : BaseFragment(), LoginFormView { override fun onResume() { super.onResume() - with(presenter) { - updateUsernameLabel() - updateSymbolInputVisibility() - } + presenter.updateUsernameLabel() } - override fun openEmail() { + override fun openEmail(lastError: String) { context?.openEmailClient( - requireContext().getString(R.string.login_email_intent_title), - "wulkanowyinc@gmail.com", - requireContext().getString(R.string.login_email_subject), - requireContext().getString(R.string.login_email_text, appInfo.systemModel, appInfo.systemVersion.toString(), appInfo.versionName) + chooserTitle = requireContext().getString(R.string.login_email_intent_title), + email = "wulkanowyinc@gmail.com", + subject = requireContext().getString(R.string.login_email_subject), + body = requireContext().getString(R.string.login_email_text, + "${appInfo.systemManufacturer} ${appInfo.systemModel}", + appInfo.systemVersion.toString(), + appInfo.versionName, + "$formHostValue/$formHostSymbol", + lastError + ) ) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt index 6d4292856..997e1e85f 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 @@ -16,6 +16,8 @@ class LoginFormPresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository, schedulers) { + private var lastError: Throwable? = null + override fun onAttachView(view: LoginFormView) { super.onAttachView(view) view.run { @@ -46,9 +48,7 @@ class LoginFormPresenter @Inject constructor( if (formHostValue.contains("fakelog")) { setCredentials("jan@fakelog.cf", "jan123") } - setSymbol(formHostSymbol) updateUsernameLabel() - updateSymbolInputVisibility() } } @@ -58,12 +58,6 @@ class LoginFormPresenter @Inject constructor( } } - fun updateSymbolInputVisibility() { - view?.run { - showSymbol("adfs" in formHostValue) - } - } - fun onPassTextChanged() { view?.clearPassError() } @@ -72,17 +66,13 @@ class LoginFormPresenter @Inject constructor( view?.clearUsernameError() } - fun onSymbolTextChanged() { - view?.clearSymbolError() - } - fun onSignInClick() { val email = view?.formUsernameValue.orEmpty().trim() val password = view?.formPassValue.orEmpty().trim() val host = view?.formHostValue.orEmpty().trim() - val symbol = view?.formSymbolValue.orEmpty().trim() + val symbol = view?.formHostSymbol.orEmpty().trim() - if (!validateCredentials(email, password, host, symbol)) return + if (!validateCredentials(email, password, host)) return disposable.add(studentRepository.getStudentsScrapper(email, password, host, symbol) .subscribeOn(schedulers.backgroundThread) @@ -109,6 +99,7 @@ class LoginFormPresenter @Inject constructor( Timber.i("Login result: An exception occurred") analytics.logEvent("registration_form", "success" to false, "students" to -1, "scrapperBaseUrl" to host, "error" to it.message.ifNullOrBlank { "No message" }) loginErrorHandler.dispatch(it) + lastError = it view?.showContact(true) })) } @@ -118,19 +109,29 @@ class LoginFormPresenter @Inject constructor( } fun onEmailClick() { - view?.openEmail() + view?.openEmail(lastError?.message.ifNullOrBlank { "none" }) } fun onRecoverClick() { view?.onRecoverClick() } - private fun validateCredentials(login: String, password: String, host: String, symbol: String): Boolean { + private fun validateCredentials(login: String, password: String, host: String): Boolean { var isCorrect = true if (login.isEmpty()) { view?.setErrorUsernameRequired() isCorrect = false + } else { + if ("@" in login && "standard" !in host) { + view?.setErrorLoginRequired() + isCorrect = false + } + + if ("@" !in login && "standard" in host) { + view?.setErrorEmailRequired() + isCorrect = false + } } if (password.isEmpty()) { @@ -143,11 +144,6 @@ class LoginFormPresenter @Inject constructor( isCorrect = false } - if ("standard" !in host && symbol.isBlank()) { - view?.setErrorSymbolRequired(focus = isCorrect) - isCorrect = false - } - return isCorrect } } 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 4a570f5cf..6506fa88f 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 @@ -15,23 +15,19 @@ interface LoginFormView : BaseView { val formHostSymbol: String - val formSymbolValue: String - val nicknameLabel: String val emailLabel: String fun setCredentials(username: String, pass: String) - fun setSymbol(symbol: String) - fun setUsernameLabel(label: String) - fun showSymbol(show: Boolean) - fun setErrorUsernameRequired() - fun setErrorSymbolRequired(focus: Boolean) + fun setErrorLoginRequired() + + fun setErrorEmailRequired() fun setErrorPassRequired(focus: Boolean) @@ -43,8 +39,6 @@ interface LoginFormView : BaseView { fun clearPassError() - fun clearSymbolError() - fun showSoftKeyboard() fun hideSoftKeyboard() @@ -63,7 +57,7 @@ interface LoginFormView : BaseView { fun openFaqPage() - fun openEmail() + fun openEmail(lastError: String) fun openAdvancedLogin() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt index 6e8a83c73..1bff49262 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt @@ -11,7 +11,6 @@ import android.view.ViewGroup import android.webkit.JavascriptInterface import android.webkit.WebView import android.webkit.WebViewClient -import android.widget.ArrayAdapter import androidx.core.widget.doOnTextChanged import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseFragment @@ -35,15 +34,17 @@ class LoginRecoverFragment : BaseFragment(), LoginRecoverView { private lateinit var hostValues: Array + private lateinit var hostSymbols: Array + override val recoverHostValue: String get() = hostValues.getOrNull(hostKeys.indexOf(loginRecoverHost.text.toString())).orEmpty() + override val formHostSymbol: String + get() = hostSymbols.getOrNull(hostKeys.indexOf(loginRecoverHost.text.toString())).orEmpty() + override val recoverNameValue: String get() = loginRecoverName.text.toString().trim() - override val recoverSymbolValue: String - get() = loginRecoverSymbol.text.toString().trim() - override val emailHintString: String get() = getString(R.string.login_email_hint) @@ -66,17 +67,15 @@ class LoginRecoverFragment : BaseFragment(), LoginRecoverView { loginRecoverWebView.setBackgroundColor(Color.TRANSPARENT) hostKeys = resources.getStringArray(R.array.hosts_keys) hostValues = resources.getStringArray(R.array.hosts_values) + hostSymbols = resources.getStringArray(R.array.hosts_symbols) loginRecoverName.doOnTextChanged { _, _, _, _ -> presenter.onNameTextChanged() } - loginRecoverSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() } loginRecoverHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() } loginRecoverButton.setOnClickListener { presenter.onRecoverClick() } loginRecoverErrorRetry.setOnClickListener { presenter.onRecoverClick() } loginRecoverErrorDetails.setOnClickListener { presenter.onDetailsClick() } loginRecoverLogin.setOnClickListener { (activity as LoginActivity).switchView(0) } - loginRecoverSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values))) - with(loginRecoverHost) { setText(hostKeys.getOrNull(0).orEmpty()) setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys)) @@ -106,25 +105,10 @@ class LoginRecoverFragment : BaseFragment(), LoginRecoverView { } } - override fun setSymbolError(focus: Boolean) { - with(loginRecoverSymbolLayout) { - if (focus) requestFocus() - error = getString(R.string.login_field_required) - } - } - override fun clearUsernameError() { loginRecoverNameLayout.error = null } - override fun clearSymbolError() { - loginRecoverSymbolLayout.error = null - } - - override fun showSymbol(show: Boolean) { - loginRecoverSymbolLayout.visibility = if (show) VISIBLE else GONE - } - override fun showProgress(show: Boolean) { loginRecoverProgress.visibility = if (show) VISIBLE else GONE } 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 7016d1734..9a21fb910 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 @@ -34,10 +34,6 @@ class LoginRecoverPresenter @Inject constructor( view?.clearUsernameError() } - fun onSymbolTextChanged() { - view?.clearSymbolError() - } - fun onHostSelected() { view?.run { if ("fakelog" in recoverHostValue) setDefaultCredentials("jan@fakelog.cf") @@ -48,7 +44,6 @@ class LoginRecoverPresenter @Inject constructor( fun updateFields() { view?.run { - showSymbol("adfs" in recoverHostValue) setUsernameHint(if ("standard" in recoverHostValue) emailHintString else loginPeselEmailHintString) } } @@ -56,9 +51,9 @@ class LoginRecoverPresenter @Inject constructor( fun onRecoverClick() { val username = view?.recoverNameValue.orEmpty() val host = view?.recoverHostValue.orEmpty() - val symbol = view?.recoverSymbolValue.orEmpty() + val symbol = view?.formHostSymbol.orEmpty() - if (!validateInput(username, host, symbol)) return + if (!validateInput(username, host)) return disposable.add(recoverRepository.getReCaptchaSiteKey(host, symbol.ifBlank { "Default" }) .subscribeOn(schedulers.backgroundThread) @@ -75,12 +70,12 @@ class LoginRecoverPresenter @Inject constructor( .subscribe({ (resetUrl, siteKey) -> view?.loadReCaptcha(siteKey, resetUrl) }) { - Timber.e("Obtain captcha site key result: An exception occurred") + Timber.i("Obtain captcha site key result: An exception occurred") errorHandler.dispatch(it) }) } - private fun validateInput(username: String, host: String, symbol: String): Boolean { + private fun validateInput(username: String, host: String): Boolean { var isCorrect = true if (username.isEmpty()) { @@ -93,18 +88,13 @@ class LoginRecoverPresenter @Inject constructor( isCorrect = false } - if ("adfs" in host && symbol.isBlank()) { - view?.setSymbolError(focus = isCorrect) - isCorrect = false - } - return isCorrect } fun onReCaptchaVerified(reCaptchaResponse: String) { val username = view?.recoverNameValue.orEmpty() val host = view?.recoverHostValue.orEmpty() - val symbol = view?.recoverSymbolValue.ifNullOrBlank { "Default" } + val symbol = view?.formHostSymbol.ifNullOrBlank { "Default" } with(disposable) { clear() @@ -130,7 +120,7 @@ class LoginRecoverPresenter @Inject constructor( analytics.logEvent("account_recover", "register" to host, "symbol" to symbol, "success" to true) }) { - Timber.e("Send recover request result: An exception occurred") + Timber.i("Send recover request result: An exception occurred") errorHandler.dispatch(it) analytics.logEvent("account_recover", "register" to host, "symbol" to symbol, "success" to false) }) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverView.kt index 693c52a3c..2e21d3351 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverView.kt @@ -6,9 +6,9 @@ interface LoginRecoverView : BaseView { val recoverHostValue: String - val recoverNameValue: String + val formHostSymbol: String - val recoverSymbolValue: String + val recoverNameValue: String val emailHintString: String @@ -22,18 +22,12 @@ interface LoginRecoverView : BaseView { fun clearUsernameError() - fun clearSymbolError() - - fun showSymbol(show: Boolean) - fun setErrorNameRequired() fun setUsernameHint(hint: String) fun setUsernameError(message: String) - fun setSymbolError(focus: Boolean) - fun showSoftKeyboard() fun hideSoftKeyboard() 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 ef81c410b..110dd82e5 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,7 +1,7 @@ package io.github.wulkanowy.ui.modules.login.recover import android.content.res.Resources -import com.readystatesoftware.chuck.api.ChuckCollector +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 @@ -10,8 +10,8 @@ import javax.inject.Inject class RecoverErrorHandler @Inject constructor( resources: Resources, - chuckCollector: ChuckCollector -) : ErrorHandler(resources, chuckCollector) { + chuckerCollector: ChuckerCollector +) : ErrorHandler(resources, chuckerCollector) { var onInvalidUsername: (String) -> Unit = {} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt index 8478f7ccf..9860af22f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt @@ -101,12 +101,17 @@ class LoginStudentSelectFragment : BaseFragment(), LoginStudentSelectView { context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) } - override fun openEmail() { + override fun openEmail(lastError: String) { context?.openEmailClient( - requireContext().getString(R.string.login_email_intent_title), - "wulkanowyinc@gmail.com", - requireContext().getString(R.string.login_email_subject), - requireContext().getString(R.string.login_email_text, appInfo.systemModel, appInfo.systemVersion.toString(), appInfo.versionName) + chooserTitle = requireContext().getString(R.string.login_email_intent_title), + email = "wulkanowyinc@gmail.com", + subject = requireContext().getString(R.string.login_email_subject), + body = requireContext().getString(R.string.login_email_text, appInfo.systemModel, + appInfo.systemVersion.toString(), + appInfo.versionName, + "Select users to log in", + lastError + ) ) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt index 87acebf1e..82de5a887 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 @@ -8,7 +8,6 @@ import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.ifNullOrBlank -import io.reactivex.Single import timber.log.Timber import java.io.Serializable import javax.inject.Inject @@ -20,6 +19,8 @@ class LoginStudentSelectPresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository, schedulers) { + private var lastError: Throwable? = null + var students = emptyList() private var selectedStudents = mutableListOf() @@ -83,6 +84,7 @@ class LoginStudentSelectPresenter @Inject constructor( }) }, { errorHandler.dispatch(it) + lastError = it view?.updateData(students.map { student -> LoginStudentSelectItem(student, false) }) }) ) @@ -109,6 +111,7 @@ class LoginStudentSelectPresenter @Inject constructor( students.forEach { analytics.logEvent("registration_student_select", "success" to false, "scrapperBaseUrl" to it.scrapperBaseUrl, "symbol" to it.symbol, "error" to error.message.ifNullOrBlank { "No message" }) } Timber.i("Registration result: An exception occurred ") loginErrorHandler.dispatch(error) + lastError = error view?.apply { showProgress(false) showContent(true) @@ -122,6 +125,6 @@ class LoginStudentSelectPresenter @Inject constructor( } fun onEmailClick() { - view?.openEmail() + view?.openEmail(lastError?.message.ifNullOrBlank { "empty" }) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt index 238771009..d80b059db 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectView.kt @@ -20,5 +20,5 @@ interface LoginStudentSelectView : BaseView { fun openDiscordInvite() - fun openEmail() + fun openEmail(lastError: String) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt index 5e9ce3ba3..5602b6248 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt @@ -130,12 +130,18 @@ class LoginSymbolFragment : BaseFragment(), LoginSymbolView { context?.openInternetBrowser("https://wulkanowy.github.io/czesto-zadawane-pytania/co-to-jest-symbol", ::showMessage) } - override fun openEmail() { + override fun openEmail(host: String, lastError: String) { context?.openEmailClient( - requireContext().getString(R.string.login_email_intent_title), - "wulkanowyinc@gmail.com", - requireContext().getString(R.string.login_email_subject), - requireContext().getString(R.string.login_email_text, appInfo.systemModel, appInfo.systemVersion.toString(), appInfo.versionName) + chooserTitle = requireContext().getString(R.string.login_email_intent_title), + email = "wulkanowyinc@gmail.com", + subject = requireContext().getString(R.string.login_email_subject), + body = requireContext().getString(R.string.login_email_text, + "${appInfo.systemManufacturer} ${appInfo.systemModel}", + appInfo.systemVersion.toString(), + appInfo.versionName, + "$host/${loginSymbolName.text}", + lastError + ) ) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt index ee6c30c3b..1f9e66b70 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 @@ -18,6 +18,8 @@ class LoginSymbolPresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository, schedulers) { + private var lastError: Throwable? = null + var loginData: Triple? = null @Suppress("UNCHECKED_CAST") @@ -37,13 +39,15 @@ class LoginSymbolPresenter @Inject constructor( } fun attemptLogin(symbol: String) { + if (loginData == null) throw IllegalArgumentException("Login data is null") + if (symbol.isBlank()) { view?.setErrorSymbolRequire() return } disposable.add( - Single.fromCallable { if (loginData == null) throw IllegalArgumentException("Login data is null") else loginData } + Single.fromCallable { loginData } .flatMap { studentRepository.getStudentsScrapper(it.first, it.second, it.third, symbol) } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) @@ -77,6 +81,7 @@ class LoginSymbolPresenter @Inject constructor( Timber.i("Login with symbol result: An exception occurred") analytics.logEvent("registration_symbol", "success" to false, "students" to -1, "scrapperBaseUrl" to loginData?.third, "symbol" to symbol, "error" to it.message.ifNullOrBlank { "No message" }) loginErrorHandler.dispatch(it) + lastError = it view?.showContact(true) })) } @@ -94,6 +99,6 @@ class LoginSymbolPresenter @Inject constructor( } fun onEmailClick() { - view?.openEmail() + view?.openEmail(loginData?.third.orEmpty(), lastError?.message.ifNullOrBlank { "empty" }) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt index 1afc15327..6938bf138 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt @@ -31,5 +31,5 @@ interface LoginSymbolView : BaseView { fun openFaqPage() - fun openEmail() + fun openEmail(host: String, lastError: String) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt index dbcba4306..1610d0298 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt @@ -20,6 +20,7 @@ import io.github.wulkanowy.ui.modules.exam.ExamFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeModule import io.github.wulkanowy.ui.modules.homework.HomeworkFragment +import io.github.wulkanowy.ui.modules.homework.details.HomeworkDetailsDialog import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.MessageModule @@ -98,6 +99,10 @@ abstract class MainModule { @ContributesAndroidInjector abstract fun bindHomeworkFragment(): HomeworkFragment + @PerFragment + @ContributesAndroidInjector + abstract fun bindHomeworkDetailsDialog(): HomeworkDetailsDialog + @PerFragment @ContributesAndroidInjector abstract fun bindLuckyNumberFragment(): LuckyNumberFragment @@ -127,7 +132,7 @@ abstract class MainModule { abstract fun bindLogViewerFragment(): LogViewerFragment @PerFragment - @ContributesAndroidInjector() + @ContributesAndroidInjector abstract fun bindContributorFragment(): ContributorFragment @PerFragment 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 e6c299171..f5a490044 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 @@ -33,7 +33,7 @@ class MainPresenter @Inject constructor( Timber.i("Main view was initialized with $startMenuIndex menu index and $startMenuMoreIndex more index") } - syncManager.startSyncWorker() + syncManager.startPeriodicSyncWorker() analytics.logEvent("app_open", "destination" to initMenu?.name) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt index 19d6ad1de..7e52233c9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt @@ -39,6 +39,9 @@ class MessageItem(val message: Message, private val noSubjectString: String) : text = message.date.toFormattedString() setTypeface(null, style) } + with(messageItemAttachmentIcon) { + visibility = if (message.hasAttachments) View.VISIBLE else View.GONE + } } } @@ -56,7 +59,8 @@ class MessageItem(val message: Message, private val noSubjectString: String) : return message.hashCode() } - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : + FlexibleViewHolder(view, adapter), LayoutContainer { override val containerView: View get() = contentView } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt new file mode 100644 index 000000000..a0ac6ec70 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt @@ -0,0 +1,88 @@ +package io.github.wulkanowy.ui.modules.message.preview + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import io.github.wulkanowy.R +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.repositories.message.MessageFolder +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.toFormattedString +import kotlinx.android.synthetic.main.item_message_attachment.view.* +import kotlinx.android.synthetic.main.item_message_preview.view.* +import javax.inject.Inject + +class MessagePreviewAdapter @Inject constructor() : + RecyclerView.Adapter() { + + enum class ViewType(val id: Int) { + MESSAGE(1), + DIVIDER(2), + ATTACHMENT(3) + } + + var messageWithAttachment: MessageWithAttachment? = null + set(value) { + field = value + attachments = value?.attachments.orEmpty() + } + + private var attachments: List = emptyList() + + override fun getItemCount() = if (messageWithAttachment == null) 0 else attachments.size + 1 + if (attachments.isNotEmpty()) 1 else 0 + + override fun getItemViewType(position: Int) = when (position) { + 0 -> ViewType.MESSAGE.id + 1 -> ViewType.DIVIDER.id + else -> ViewType.ATTACHMENT.id + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + + return when (viewType) { + ViewType.MESSAGE.id -> MessageViewHolder(inflater.inflate(R.layout.item_message_preview, parent, false)) + ViewType.DIVIDER.id -> DividerViewHolder(inflater.inflate(R.layout.item_message_divider, parent, false)) + ViewType.ATTACHMENT.id -> AttachmentViewHolder(inflater.inflate(R.layout.item_message_attachment, parent, false)) + else -> throw IllegalStateException() + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is MessageViewHolder -> bindMessage(holder.view, requireNotNull(messageWithAttachment).message) + is AttachmentViewHolder -> bindAttachment(holder.view, requireNotNull(messageWithAttachment).attachments[position - 2]) + } + } + + @SuppressLint("SetTextI18n") + private fun bindMessage(view: View, message: Message) { + with(view) { + messagePreviewSubject.text = if (message.subject.isNotBlank()) message.subject else context.getString(R.string.message_no_subject) + messagePreviewDate.text = context.getString(R.string.message_date, message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) + messagePreviewContent.text = message.content + messagePreviewAuthor.text = if (message.folderId == MessageFolder.SENT.id) "${context.getString(R.string.message_to)} ${message.recipient}" + else "${context.getString(R.string.message_from)} ${message.sender}" + } + } + + private fun bindAttachment(view: View, attachment: MessageAttachment) { + with(view) { + messagePreviewAttachment.visibility = View.VISIBLE + messagePreviewAttachment.text = attachment.filename + setOnClickListener { + context.openInternetBrowser(attachment.url) { } + } + } + } + + class MessageViewHolder(val view: View) : RecyclerView.ViewHolder(view) + + class DividerViewHolder(val view: View) : RecyclerView.ViewHolder(view) + + class AttachmentViewHolder(val view: View) : RecyclerView.ViewHolder(view) +} 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 22231e429..953238c08 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.message.preview -import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.Menu @@ -10,8 +9,10 @@ import android.view.View import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView @@ -25,6 +26,9 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl @Inject lateinit var presenter: MessagePreviewPresenter + @Inject + lateinit var previewAdapter: MessagePreviewAdapter + private var menuReplyButton: MenuItem? = null private var menuForwardButton: MenuItem? = null @@ -34,18 +38,15 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl override val titleStringId: Int get() = R.string.message_title - override val noSubjectString: String - get() = getString(R.string.message_no_subject) - override val deleteMessageSuccessString: String get() = getString(R.string.message_delete_success) companion object { const val MESSAGE_ID_KEY = "message_id" - fun newInstance(messageId: Long): MessagePreviewFragment { + fun newInstance(message: Message): MessagePreviewFragment { return MessagePreviewFragment().apply { - arguments = Bundle().apply { putLong(MESSAGE_ID_KEY, messageId) } + arguments = Bundle().apply { putSerializable(MESSAGE_ID_KEY, message) } } } } @@ -62,11 +63,16 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) messageContainer = messagePreviewContainer - presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getLong(MESSAGE_ID_KEY) ?: 0L) + presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as? Message) } override fun initView() { messagePreviewErrorDetails.setOnClickListener { presenter.onDetailsClick() } + + with(messagePreviewRecycler) { + layoutManager = LinearLayoutManager(context) + adapter = previewAdapter + } } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -86,26 +92,11 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl } } - override fun setSubject(subject: String) { - messagePreviewSubject.text = subject - } - - @SuppressLint("SetTextI18n") - override fun setRecipient(recipient: String) { - messagePreviewAuthor.text = "${getString(R.string.message_to)} $recipient" - } - - @SuppressLint("SetTextI18n") - override fun setSender(sender: String) { - messagePreviewAuthor.text = "${getString(R.string.message_from)} $sender" - } - - override fun setDate(date: String) { - messagePreviewDate.text = getString(R.string.message_date, date) - } - - override fun setContent(content: String) { - messagePreviewContent.text = content + override fun setMessageWithAttachment(item: MessageWithAttachment) { + with(previewAdapter) { + messageWithAttachment = item + notifyDataSetChanged() + } } override fun showProgress(show: Boolean) { @@ -113,7 +104,7 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl } override fun showContent(show: Boolean) { - messagePreviewContentContainer.visibility = if (show) VISIBLE else GONE + messagePreviewRecycler.visibility = if (show) VISIBLE else GONE } override fun showOptions(show: Boolean) { @@ -159,8 +150,8 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl } override fun onSaveInstanceState(outState: Bundle) { + outState.putSerializable(MESSAGE_ID_KEY, presenter.message) super.onSaveInstanceState(outState) - outState.putLong(MESSAGE_ID_KEY, presenter.messageId) } override fun onDestroyView() { 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 7b7404b86..62ac5a532 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -1,14 +1,12 @@ package io.github.wulkanowy.ui.modules.message.preview 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.student.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.SchedulersProvider -import io.github.wulkanowy.utils.toFormattedString import timber.log.Timber import javax.inject.Inject @@ -20,61 +18,52 @@ class MessagePreviewPresenter @Inject constructor( private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler, studentRepository, schedulers) { - var messageId = 0L - - private var message: Message? = null + var message: Message? = null private lateinit var lastError: Throwable private var retryCallback: () -> Unit = {} - fun onAttachView(view: MessagePreviewView, id: Long) { + fun onAttachView(view: MessagePreviewView, message: Message?) { super.onAttachView(view) view.initView() errorHandler.showErrorMessage = ::showErrorViewOnError - loadData(id) + this.message = message + loadData(requireNotNull(message)) } - private fun onMessageLoadRetry() { + private fun onMessageLoadRetry(message: Message) { view?.run { showErrorView(false) showProgress(true) } - loadData(messageId) + loadData(message) } fun onDetailsClick() { view?.showErrorDetailsDialog(lastError) } - private fun loadData(id: Long) { - Timber.i("Loading message $id preview started") - messageId = id + private fun loadData(message: Message) { + Timber.i("Loading message ${message.messageId} preview started") disposable.apply { clear() - add(studentRepository.getCurrentStudent() - .flatMap { messageRepository.getMessage(it, messageId, true) } + add(studentRepository.getStudentById(message.studentId) + .flatMap { messageRepository.getMessage(it, message, true) } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doFinally { view?.showProgress(false) } .subscribe({ message -> - Timber.i("Loading message $id preview result: Success ") - this@MessagePreviewPresenter.message = message - view?.run { - message.let { - setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString) - setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) - setContent(it.content) - initOptions() - - if (it.folderId == MessageFolder.SENT.id) setRecipient(it.recipient) - else setSender(it.sender) - } + Timber.i("Loading message ${message.message.messageId} preview result: Success ") + this@MessagePreviewPresenter.message = message.message + view?.apply { + setMessageWithAttachment(message) + initOptions() } - analytics.logEvent("load_message_preview", "length" to message.content.length) + analytics.logEvent("load_message_preview", "length" to message.message.content.length) }) { - Timber.i("Loading message $id preview result: An exception occurred ") - retryCallback = { onMessageLoadRetry() } + Timber.i("Loading message ${message.messageId} preview result: An exception occurred ") + retryCallback = { onMessageLoadRetry(message) } errorHandler.dispatch(it) }) } @@ -96,7 +85,8 @@ class MessagePreviewPresenter @Inject constructor( private fun deleteMessage() { message?.let { message -> - disposable.add(messageRepository.deleteMessage(message) + disposable.add(studentRepository.getCurrentStudent() + .flatMap { messageRepository.deleteMessage(it, message) } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doOnSubscribe { @@ -119,8 +109,6 @@ class MessagePreviewPresenter @Inject constructor( }, { error -> retryCallback = { onMessageDelete() } errorHandler.dispatch(error) - }, { - view?.showErrorView(true) }) ) } 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 d57766273..3d620459c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -1,25 +1,16 @@ package io.github.wulkanowy.ui.modules.message.preview import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.ui.base.BaseView interface MessagePreviewView : BaseView { - val noSubjectString: String - val deleteMessageSuccessString: String fun initView() - fun setSubject(subject: String) - - fun setRecipient(recipient: String) - - fun setSender(sender: String) - - fun setDate(date: String) - - fun setContent(content: String) + fun setMessageWithAttachment(item: MessageWithAttachment) fun showProgress(show: Boolean) 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 7b15da713..232b05d79 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 @@ -147,7 +147,7 @@ class SendMessageActivity : BaseActivity(), SendMessageVie } override fun popView() { - onBackPressed() + finish() } private fun setUpExtendedHitArea() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt index 3b89b5cae..aec79f9db 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt @@ -135,12 +135,12 @@ class SendMessagePresenter @Inject constructor( if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(selectedRecipientChips) showContent(true) } else { - Timber.e("Loading recipients result: Can't find the reporting unit") + Timber.i("Loading recipients result: Can't find the reporting unit") view?.showEmpty(true) } } }, { - Timber.e("Loading recipients result: An exception occurred") + Timber.i("Loading recipients result: An exception occurred") view?.showContent(true) errorHandler.dispatch(it) })) @@ -148,7 +148,8 @@ class SendMessagePresenter @Inject constructor( private fun sendMessage(subject: String, content: String, recipients: List) { Timber.i("Sending message started") - disposable.add(messageRepository.sendMessage(subject, content, recipients) + disposable.add(studentRepository.getCurrentStudent() + .flatMap { messageRepository.sendMessage(it, subject, content, recipients) } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doOnSubscribe { 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 d6065983d..ce0fc7805 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,6 +12,7 @@ import eu.davidea.flexibleadapter.common.FlexibleItemDecoration import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem 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.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity @@ -116,8 +117,8 @@ class MessageTabFragment : BaseFragment(), MessageTabView { messageTabSwipe.isRefreshing = show } - override fun openMessage(messageId: Long) { - (activity as? MainActivity)?.pushView(MessagePreviewFragment.newInstance(messageId)) + override fun openMessage(message: Message) { + (activity as? MainActivity)?.pushView(MessagePreviewFragment.newInstance(message)) } override fun notifyParentDataLoaded() { 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 dc199207a..cbdb89b2e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -1,7 +1,6 @@ package io.github.wulkanowy.ui.modules.message.tab import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -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 @@ -63,11 +62,10 @@ class MessageTabPresenter @Inject constructor( if (item is MessageItem) { Timber.i("Select message ${item.message.id} item") view?.run { - openMessage(item.message.id) + openMessage(item.message) if (item.message.unread) { item.message.unread = false updateItem(item) - updateMessage(item.message) } } } @@ -119,16 +117,4 @@ class MessageTabPresenter @Inject constructor( } else showError(message, error) } } - - private fun updateMessage(message: Message) { - Timber.i("Attempt to update message ${message.id}") - disposable.add(messageRepository.updateMessage(message) - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .subscribe({ Timber.d("Update message ${message.id} result: Success") }) - { error -> - Timber.i("Update message ${message.id} result: An exception occurred") - errorHandler.dispatch(error) - }) - } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt index e5705c739..92115ed33 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt @@ -1,6 +1,7 @@ package io.github.wulkanowy.ui.modules.message.tab import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.ui.base.BaseView import io.github.wulkanowy.ui.modules.message.MessageItem @@ -32,7 +33,7 @@ interface MessageTabView : BaseView { fun showRefresh(show: Boolean) - fun openMessage(messageId: Long) + fun openMessage(message: Message) fun notifyParentDataLoaded() } 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 e95d2ce0e..a6a83f8a9 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 @@ -49,8 +49,11 @@ class MobileDevicePresenter @Inject constructor( private fun loadData(forceRefresh: Boolean = false) { Timber.i("Loading mobile devices data started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { mobileDeviceRepository.getDevices(it, forceRefresh) } + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + mobileDeviceRepository.getDevices(student, semester, forceRefresh) + } + } .map { items -> items.map { MobileDeviceItem(it) } } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) @@ -107,10 +110,11 @@ class MobileDevicePresenter @Inject constructor( fun onUnregisterConfirmed(device: MobileDevice) { Timber.i("Unregister device started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { semester -> - mobileDeviceRepository.unregisterDevice(semester, device) - .flatMap { mobileDeviceRepository.getDevices(semester, it) } + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + mobileDeviceRepository.unregisterDevice(student, semester, device) + .flatMap { mobileDeviceRepository.getDevices(student, semester, it) } + } } .map { items -> items.map { MobileDeviceItem(it) } } .subscribeOn(schedulers.backgroundThread) 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 a778cbeda..6a2a6b98b 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 @@ -29,8 +29,11 @@ class MobileDeviceTokenPresenter @Inject constructor( private fun loadData() { Timber.i("Mobile device registration data started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { mobileDeviceRepository.getToken(it) } + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + mobileDeviceRepository.getToken(student, semester) + } + } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doFinally { view?.hideLoading() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt index 492aeab26..20ffea4eb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteDialog.kt @@ -1,14 +1,18 @@ package io.github.wulkanowy.ui.modules.note +import android.annotation.SuppressLint import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.content.ContextCompat import androidx.fragment.app.DialogFragment import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.toFormattedString import kotlinx.android.synthetic.main.dialog_note.* +import io.github.wulkanowy.sdk.scrapper.notes.Note.CategoryType class NoteDialog : DialogFragment() { @@ -36,6 +40,7 @@ class NoteDialog : DialogFragment() { return inflater.inflate(R.layout.dialog_note, container, false) } + @SuppressLint("SetTextI18n") override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) @@ -43,6 +48,16 @@ class NoteDialog : DialogFragment() { noteDialogCategory.text = note.category noteDialogTeacher.text = note.teacher noteDialogContent.text = note.content + if (note.isPointsShow) { + with(noteDialogPoints) { + text = "${if (note.points > 0) "+" else ""}${note.points}" + setTextColor(when (CategoryType.getByValue(note.categoryType)) { + CategoryType.POSITIVE -> ContextCompat.getColor(requireContext(), R.color.note_positive) + CategoryType.NEGATIVE -> ContextCompat.getColor(requireContext(), R.color.note_negative) + else -> requireContext().getThemeAttrColor(android.R.attr.textColorPrimary) + }) + } + } noteDialogClose.setOnClickListener { dismiss() } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt index dabeef74a..53fe6fa91 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteItem.kt @@ -1,14 +1,20 @@ package io.github.wulkanowy.ui.modules.note +import android.annotation.SuppressLint import android.graphics.Typeface.BOLD import android.graphics.Typeface.NORMAL import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import androidx.core.content.ContextCompat import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.viewholders.FlexibleViewHolder import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Note +import io.github.wulkanowy.sdk.scrapper.notes.Note.CategoryType +import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.toFormattedString import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.item_note.* @@ -17,20 +23,30 @@ class NoteItem(val note: Note) : AbstractFlexibleItem() { override fun getLayoutRes() = R.layout.item_note - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): NoteItem.ViewHolder { - return NoteItem.ViewHolder(view, adapter) + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { + return ViewHolder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: NoteItem.ViewHolder, position: Int, payloads: MutableList?) { + @SuppressLint("SetTextI18n") + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { holder.apply { - noteItemDate.apply { + with(noteItemDate) { text = note.date.toFormattedString() setTypeface(null, if (note.isRead) NORMAL else BOLD) } - noteItemType.apply { + with(noteItemType) { text = note.category setTypeface(null, if (note.isRead) NORMAL else BOLD) } + with(noteItemPoints) { + text = "${if (note.points > 0) "+" else ""}${note.points}" + visibility = if (note.isPointsShow) VISIBLE else GONE + setTextColor(when(CategoryType.getByValue(note.categoryType)) { + CategoryType.POSITIVE -> ContextCompat.getColor(context, R.color.note_positive) + CategoryType.NEGATIVE -> ContextCompat.getColor(context, R.color.note_negative) + else -> context.getThemeAttrColor(android.R.attr.textColorPrimary) + }) + } noteItemTeacher.text = note.teacher noteItemContent.text = note.content } @@ -53,7 +69,8 @@ class NoteItem(val note: Note) : AbstractFlexibleItem() { return result } - class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter), LayoutContainer { + class ViewHolder(val view: View, adapter: FlexibleAdapter<*>) : + FlexibleViewHolder(view, adapter), LayoutContainer { override val containerView: View get() = contentView } 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 d3299d90d..e2eb614dc 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 @@ -64,8 +64,11 @@ class SchoolPresenter @Inject constructor( private fun loadData(forceRefresh: Boolean = false) { Timber.i("Loading school info started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMapMaybe { schoolRepository.getSchoolInfo(it, forceRefresh) } + .flatMapMaybe { student -> + semesterRepository.getCurrentSemester(student).flatMapMaybe { + schoolRepository.getSchoolInfo(student, it, forceRefresh) + } + } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doFinally { 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 6aa4871d2..c5b6cfd69 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 @@ -52,8 +52,11 @@ class TeacherPresenter @Inject constructor( private fun loadData(forceRefresh: Boolean = false) { Timber.i("Loading teachers data started") disposable.add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } - .flatMap { teacherRepository.getTeachers(it, forceRefresh) } + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + teacherRepository.getTeachers(student, semester, forceRefresh) + } + } .map { it.filter { teacher -> teacher.name.isNotBlank() } } .map { items -> items.map { TeacherItem(it, view?.noSubjectString.orEmpty()) } } .subscribeOn(schedulers.backgroundThread) 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 03a89d274..fe0a97632 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 @@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.settings import android.content.Context import android.content.SharedPreferences import android.os.Bundle +import androidx.appcompat.app.AlertDialog import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.yariksoffice.lingver.Lingver @@ -33,11 +34,24 @@ class SettingsFragment : PreferenceFragmentCompat(), override val titleStringId get() = R.string.settings_title + override val syncSuccessString get() = getString(R.string.pref_services_message_sync_success) + + override val syncFailedString get() = getString(R.string.pref_services_message_sync_failed) + override fun onAttach(context: Context) { AndroidSupportInjection.inject(this) super.onAttach(context) } + override fun initView() { + findPreference(getString(R.string.pref_key_services_force_sync))?.run { + onPreferenceClickListener = Preference.OnPreferenceClickListener { + presenter.onSyncNowClicked() + true + } + } + } + override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) presenter.onAttachView(this) @@ -61,12 +75,21 @@ class SettingsFragment : PreferenceFragmentCompat(), } override fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) { - findPreference(serviceEnablesKey)?.apply { + findPreference(serviceEnablesKey)?.run { summary = if (isHolidays) getString(R.string.pref_services_suspended) else "" isEnabled = !isHolidays } } + override fun setSyncInProgress(inProgress: Boolean) { + if (activity == null || !isAdded) return + + findPreference(getString(R.string.pref_key_services_force_sync))?.run { + isEnabled = !inProgress + summary = if (inProgress) getString(R.string.pref_services_sync_in_progress) else "" + } + } + override fun showError(text: String, error: Throwable) { (activity as? BaseActivity<*>)?.showError(text, error) } @@ -87,6 +110,15 @@ class SettingsFragment : PreferenceFragmentCompat(), ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) } + override fun showForceSyncDialog() { + AlertDialog.Builder(requireContext()) + .setTitle(R.string.pref_services_dialog_force_sync_title) + .setMessage(R.string.pref_services_dialog_force_sync_summary) + .setPositiveButton(android.R.string.ok) { _, _ -> presenter.onForceSyncDialogSubmit() } + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() + } + override fun onResume() { super.onResume() preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) 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 89ba0ee5e..c8545ac0e 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 @@ -1,6 +1,7 @@ package io.github.wulkanowy.ui.modules.settings -import com.readystatesoftware.chuck.api.ChuckCollector +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.services.sync.SyncManager @@ -21,7 +22,7 @@ class SettingsPresenter @Inject constructor( private val preferencesRepository: PreferencesRepository, private val analytics: FirebaseAnalyticsHelper, private val syncManager: SyncManager, - private val chuckCollector: ChuckCollector, + private val chuckerCollector: ChuckerCollector, private val appInfo: AppInfo ) : BasePresenter(errorHandler, studentRepository, schedulers) { @@ -29,6 +30,7 @@ class SettingsPresenter @Inject constructor( super.onAttachView(view) Timber.i("Settings view was initialized") view.setServicesSuspended(preferencesRepository.serviceEnableKey, now().isHolidays) + view.initView() } fun onSharedPreferenceChanged(key: String) { @@ -36,9 +38,9 @@ class SettingsPresenter @Inject constructor( with(preferencesRepository) { when (key) { - serviceEnableKey -> with(syncManager) { if (isServiceEnabled) startSyncWorker() else stopSyncWorker() } - servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startSyncWorker(true) - isDebugNotificationEnableKey -> chuckCollector.showNotification(isDebugNotificationEnable) + serviceEnableKey -> with(syncManager) { if (isServiceEnabled) startPeriodicSyncWorker() else stopSyncWorker() } + servicesIntervalKey, servicesOnlyWifiKey -> syncManager.startPeriodicSyncWorker(true) + isDebugNotificationEnableKey -> chuckerCollector.showNotification = isDebugNotificationEnable appThemeKey -> view?.recreateView() appLanguageKey -> view?.run { updateLanguage(if (appLanguage == "system") appInfo.systemLanguage else appLanguage) @@ -49,4 +51,38 @@ class SettingsPresenter @Inject constructor( } analytics.logEvent("setting_changed", "name" to key) } + + fun onSyncNowClicked() { + view?.showForceSyncDialog() + } + + fun onForceSyncDialogSubmit() { + view?.run { + val successString = syncSuccessString + val failedString = syncFailedString + disposable.add(syncManager.startOneTimeSyncWorker() + .doOnSubscribe { + setSyncInProgress(true) + Timber.i("Setting sync now started") + analytics.logEvent("sync_now", "status" to "started") + } + .doFinally { setSyncInProgress(false) } + .subscribe({ workInfo -> + when (workInfo.state) { + WorkInfo.State.SUCCEEDED -> { + showMessage(successString) + analytics.logEvent("sync_now", "status" to "success") + } + WorkInfo.State.FAILED -> { + showError(failedString, Throwable(workInfo.outputData.getString("error"))) + analytics.logEvent("sync_now", "status" to "failed") + } + else -> Timber.d("Sync now state: ${workInfo.state}") + } + }, { + Timber.e(it, "Sync now failed") + }) + ) + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt index e50eb47b3..4a1b0c766 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsView.kt @@ -4,9 +4,19 @@ import io.github.wulkanowy.ui.base.BaseView interface SettingsView : BaseView { + val syncSuccessString: String + + val syncFailedString: String + + fun initView() + fun recreateView() fun updateLanguage(langCode: String) fun setServicesSuspended(serviceEnablesKey: String, isHolidays: Boolean) + + fun setSyncInProgress(inProgress: Boolean) + + fun showForceSyncDialog() } 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 913f6c121..2e9d0a0b3 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 @@ -134,9 +134,12 @@ class TimetablePresenter @Inject constructor( disposable.apply { clear() add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + timetableRepository.getTimetable(student, semester, currentDate, currentDate, forceRefresh) + } + } .delay(200, MILLISECONDS) - .flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) } .map { createTimetableItems(it) } .map { items -> items.sortedWith(compareBy({ it.lesson.number }, { !it.lesson.isStudentPlan })) } .subscribeOn(schedulers.backgroundThread) 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 1d043af3a..c1389cedd 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,15 @@ package io.github.wulkanowy.ui.modules.timetable.completed import android.content.res.Resources -import com.readystatesoftware.chuck.api.ChuckCollector +import com.chuckerteam.chucker.api.ChuckerCollector import io.github.wulkanowy.sdk.exception.FeatureDisabledException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject class CompletedLessonsErrorHandler @Inject constructor( resources: Resources, - chuckCollector: ChuckCollector -) : ErrorHandler(resources, chuckCollector) { + chuckerCollector: ChuckerCollector +) : ErrorHandler(resources, chuckerCollector) { var onFeatureDisabled: () -> Unit = {} 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 e2b569508..001fed97b 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 @@ -114,9 +114,12 @@ class CompletedLessonsPresenter @Inject constructor( disposable.apply { clear() add(studentRepository.getCurrentStudent() - .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { student -> + semesterRepository.getCurrentSemester(student).flatMap { semester -> + completedLessonsRepository.getCompletedLessons(student, semester, currentDate, currentDate, forceRefresh) + } + } .delay(200, TimeUnit.MILLISECONDS) - .flatMap { completedLessonsRepository.getCompletedLessons(it, currentDate, currentDate, forceRefresh) } .map { items -> items.map { CompletedLessonItem(it) } } .map { items -> items.sortedBy { it.completedLesson.number } } .subscribeOn(schedulers.backgroundThread) 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 62620b8ed..fb8c59586 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 @@ -107,8 +107,11 @@ class TimetableWidgetFactory( if (student != null) Maybe.just(student) else Maybe.empty() } - .flatMap { semesterRepository.getCurrentSemester(it).toMaybe() } - .flatMap { timetableRepository.getTimetable(it, date, date).toMaybe() } + .flatMap { student -> + semesterRepository.getCurrentSemester(student).toMaybe().flatMap { semester -> + timetableRepository.getTimetable(student, semester, date, date).toMaybe() + } + } .map { items -> items.sortedWith(compareBy({ it.number }, { !it.isStudentPlan })) } .map { lessons -> lessons.filter { if (prefRepository.showWholeClassPlan == "no") it.isStudentPlan else true } } .subscribeOn(schedulers.backgroundThread) diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt index a265378e2..582b1c835 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -32,12 +32,16 @@ fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) - } } -fun Context.openEmailClient(chooserTitle: String, email: String, subject: String?, body: String?) { - val emailIntent = Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", email, null)) - emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(email)) - if (subject != null) emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject) - if (body != null) emailIntent.putExtra(Intent.EXTRA_TEXT, body) - startActivity(Intent.createChooser(emailIntent, chooserTitle)) +fun Context.openEmailClient(chooserTitle: String, email: String, subject: String, body: String, onActivityNotFound: () -> Unit = {}) { + val intent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")).apply { + putExtra(Intent.EXTRA_EMAIL, arrayOf(email)) + putExtra(Intent.EXTRA_SUBJECT, subject) + putExtra(Intent.EXTRA_TEXT, body) + } + + if (intent.resolveActivity(packageManager) != null) { + startActivity(Intent.createChooser(intent, chooserTitle)) + } else onActivityNotFound() } fun Context.openNavigation(location: String) { 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 15de40d37..fc3528495 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/LoggerUtils.kt @@ -18,7 +18,7 @@ class DebugLogTree : Timber.DebugTree() { } } -private fun Bundle?.checkSavedState() = if (this == null) "(STATE IS NULL)" else "" +private fun Bundle?.checkSavedState() = if (this == null) "(STATE IS NULL)" else "(STATE IS NOT NULL)" class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks { diff --git a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt new file mode 100644 index 000000000..a2d0384e3 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt @@ -0,0 +1,23 @@ +package io.github.wulkanowy.utils + +import android.content.res.Resources +import io.github.wulkanowy.R +import io.github.wulkanowy.sdk.exception.FeatureDisabledException +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException +import io.github.wulkanowy.sdk.exception.NotLoggedInException +import io.github.wulkanowy.sdk.exception.PasswordChangeRequiredException +import io.github.wulkanowy.sdk.exception.ServiceUnavailableException +import java.io.InterruptedIOException +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 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) + else -> getString(R.string.error_unknown) +} diff --git a/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt new file mode 100644 index 000000000..e4d4163b4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt @@ -0,0 +1,29 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.Sdk +import timber.log.Timber + +fun Sdk.init(student: Student): Sdk { + email = student.email + password = student.password + symbol = student.symbol + schoolSymbol = student.schoolSymbol + studentId = student.studentId + classId = student.classId + + if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + scrapperBaseUrl = student.scrapperBaseUrl + loginType = Sdk.ScrapperLoginType.valueOf(student.loginType) + } + loginId = student.userLoginId + + mode = Sdk.Mode.valueOf(student.loginMode) + mobileBaseUrl = student.mobileBaseUrl + certKey = student.certificateKey + privateKey = student.privateKey + + Timber.d("Sdk in ${student.loginMode} mode reinitialized") + + return this +} 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 be1a02e7f..4941fcfa6 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,8 @@ -Wersja 0.16.0 +Wersja 0.17.2 i 0.17.3 -- oceny zapisane w nawiasach nie są już liczone do średniej -- naprawiliśmy wyświetlanie szczęśliwych numerków na kontach rodziców (może być potrzebne zalogowanie się ponownie) -- dodaliśmy opcję przywracania hasła, ulepszyliśmy formularz logowania -- dodaliśmy język ukraiński i niemiecki -- dodaliśmy informację na górnym pasku o bieżącym semestrze w widoku ocen -- ulepszyliśmy przełączanie aplikacji na nowy semestr +- naprawiliśmy wyświetlanie przycisku oznaczania zadania domowego jako wykonanego +- naprawiliśmy rzadki błąd ze stabilnością przy wysyłaniu wiadomości +- naprawiliśmy błąd po logowaniu w domyślnym trybie, jeśli wcześniej użytkownik zalogowany był w trybie Mobilnego API +- ulepszyliśmy wygląd okienka ze szczegółami błędu Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases diff --git a/app/src/main/res/drawable-hdpi/ic_stat_push.png b/app/src/main/res/drawable-hdpi/ic_stat_push.png new file mode 100644 index 000000000..84578183f Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_stat_push.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_stat_push.png b/app/src/main/res/drawable-mdpi/ic_stat_push.png new file mode 100644 index 000000000..d1e954b0c Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_stat_push.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_push.png b/app/src/main/res/drawable-xhdpi/ic_stat_push.png new file mode 100644 index 000000000..79b38e63f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_stat_push.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_push.png b/app/src/main/res/drawable-xxhdpi/ic_stat_push.png new file mode 100644 index 000000000..bc33cb5b1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_stat_push.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stat_push.png b/app/src/main/res/drawable-xxxhdpi/ic_stat_push.png new file mode 100644 index 000000000..b354bd06c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_stat_push.png differ diff --git a/app/src/main/res/drawable/ic_attachment.xml b/app/src/main/res/drawable/ic_attachment.xml new file mode 100644 index 000000000..c18714f5c --- /dev/null +++ b/app/src/main/res/drawable/ic_attachment.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_check.xml b/app/src/main/res/drawable/ic_check.xml new file mode 100644 index 000000000..3c728c59f --- /dev/null +++ b/app/src/main/res/drawable/ic_check.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml new file mode 100644 index 000000000..045bbc0c0 --- /dev/null +++ b/app/src/main/res/drawable/ic_share.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/dialog_error.xml b/app/src/main/res/layout/dialog_error.xml index ec25f6a4f..2d4e5d0ba 100644 --- a/app/src/main/res/layout/dialog_error.xml +++ b/app/src/main/res/layout/dialog_error.xml @@ -1,27 +1,49 @@ - + + + + + android:overScrollMode="ifContentScrolls" + android:paddingHorizontal="24dp" + app:layout_constrainedHeight="true" + app:layout_constraintHeight_max="300dp" + app:layout_constraintHeight_min="200dp" + app:layout_constraintTop_toTopOf="parent"> + android:id="@+id/errorDialogHorizontalScroll" + android:layout_width="match_parent" + android:layout_height="wrap_content"> - + + + + + - \ No newline at end of file + diff --git a/app/src/main/res/layout/dialog_homework.xml b/app/src/main/res/layout/dialog_homework.xml index 7ed9b795b..7c149d4b0 100644 --- a/app/src/main/res/layout/dialog_homework.xml +++ b/app/src/main/res/layout/dialog_homework.xml @@ -1,109 +1,47 @@ - - + android:layout_height="match_parent" + android:orientation="vertical"> - + + + android:background="@drawable/ic_all_divider" + android:paddingHorizontal="10dp" + android:paddingVertical="5dp"> - - - - - - - - - - - - - - - - - - - - - + android:layout_alignParentStart="true" + android:layout_alignParentBottom="true" + android:text="@string/homework_mark_as_done" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" /> - - + android:layout_alignParentEnd="true" + android:layout_alignParentBottom="true" + android:text="@string/all_close" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" /> + + diff --git a/app/src/main/res/layout/dialog_note.xml b/app/src/main/res/layout/dialog_note.xml index 0393e56af..71506b01f 100644 --- a/app/src/main/res/layout/dialog_note.xml +++ b/app/src/main/res/layout/dialog_note.xml @@ -64,6 +64,22 @@ android:textIsSelectable="true" android:textSize="12sp" /> + + + + @@ -215,36 +215,6 @@ tools:ignore="Deprecated,LabelFor" /> - - - - - + app:layout_constraintTop_toBottomOf="@+id/loginFormHostLayout" /> @@ -105,7 +106,7 @@ android:layout_marginRight="24dp" android:hint="@string/login_host_hint" android:orientation="vertical" - app:layout_constraintBottom_toTopOf="@+id/loginRecoverSymbolLayout" + app:layout_constraintBottom_toTopOf="@+id/loginRecoverButton" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/loginRecoverNameLayout"> @@ -118,46 +119,17 @@ tools:ignore="Deprecated,LabelFor" /> - - - - -