diff --git a/.travis.yml b/.travis.yml index cc45f7dbe..a5cb2ed91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ cache: branches: only: - develop - - 0.16.0 + - 0.17.0 android: licenses: diff --git a/app/build.gradle b/app/build.gradle index 86fe69666..9eeb3e7d9 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 54 + versionName "0.17.0" 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.0" 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.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-beta01" 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.4' + playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.0.4" + 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/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/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/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/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..bbaea1e34 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 @@ -23,7 +23,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..e506bdb94 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 @@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.Semester 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 @@ -36,4 +37,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/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt index 374b9a294..0ea063501 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRepository.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.data.repositories.luckynumber 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.LuckyNumber import io.github.wulkanowy.data.db.entities.Student import io.reactivex.Completable @@ -15,33 +16,36 @@ import javax.inject.Singleton class LuckyNumberRepository @Inject constructor( private val settings: InternetObservingSettings, private val local: LuckyNumberLocal, - private val remote: LuckyNumberRemote + private val remote: LuckyNumberRemote, + private val sdkHelper: SdkHelper ) { fun getLuckyNumber(student: Student, forceRefresh: Boolean = false, notify: Boolean = false): Maybe { - return local.getLuckyNumber(student, LocalDate.now()).filter { !forceRefresh } - .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) - .flatMapMaybe { - if (it) remote.getLuckyNumber(student) - else Maybe.error(UnknownHostException()) - }.flatMap { new -> - local.getLuckyNumber(student, LocalDate.now()) - .doOnSuccess { old -> - if (new != old) { - local.deleteLuckyNumber(old) + return Maybe.just(sdkHelper.init(student)).flatMap { + local.getLuckyNumber(student, LocalDate.now()).filter { !forceRefresh } + .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) + .flatMapMaybe { + if (it) remote.getLuckyNumber(student) + else Maybe.error(UnknownHostException()) + }.flatMap { new -> + local.getLuckyNumber(student, LocalDate.now()) + .doOnSuccess { old -> + if (new != old) { + local.deleteLuckyNumber(old) + local.saveLuckyNumber(new.apply { + if (notify) isNotified = false + }) + } + } + .doOnComplete { local.saveLuckyNumber(new.apply { if (notify) isNotified = false }) } - } - .doOnComplete { - local.saveLuckyNumber(new.apply { - if (notify) isNotified = false - }) - } - }.flatMap({ local.getLuckyNumber(student, LocalDate.now()) }, { Maybe.error(it) }, - { local.getLuckyNumber(student, LocalDate.now()) }) - ) + }.flatMap({ local.getLuckyNumber(student, LocalDate.now()) }, { Maybe.error(it) }, + { local.getLuckyNumber(student, LocalDate.now()) }) + ) + } } fun getNotNotifiedLuckyNumber(student: Student): Maybe { 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..488522109 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,6 +1,7 @@ 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 @@ -33,14 +34,25 @@ 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(message: Message, markAsRead: Boolean = false): Single>> { + return sdk.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 { 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..396d5f689 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 @@ -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.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 +12,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 @@ -48,30 +48,31 @@ class MessageRepository @Inject constructor( } } - fun getMessage(student: Student, messageDbId: Long, markAsRead: Boolean = false): Single { + fun getMessage(student: Student, message: Message, markAsRead: Boolean = false): Single { return Single.just(sdkHelper.init(student)) .flatMap { _ -> - local.getMessage(messageDbId) + local.getMessageWithAttachment(student, message) .filter { - it.content.isNotEmpty().also { status -> + 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.getMessage(messageDbId) + if (it) local.getMessageWithAttachment(student, message) 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 } + remote.getMessagesContentDetails(dbMessage.message, markAsRead).doOnSuccess { (downloadedMessage, attachments) -> + local.updateMessages(listOf(dbMessage.message.copy(unread = !markAsRead).apply { + id = dbMessage.message.id + content = content.ifBlank { downloadedMessage } })) - Timber.d("Message $messageDbId with blank content: ${dbMessage.content.isBlank()}, marked as read") + local.saveMessageAttachments(attachments) + Timber.d("Message ${message.messageId} with blank content: ${dbMessage.message.content.isBlank()}, marked as read") } }.flatMap { - local.getMessage(messageDbId) + local.getMessageWithAttachment(student, message) } ) } @@ -83,10 +84,6 @@ 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) } } @@ -99,13 +96,12 @@ class MessageRepository @Inject constructor( } } - fun deleteMessage(message: Message): Maybe { + fun deleteMessage(message: Message): Single { return ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { if (it) remote.deleteMessage(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/note/NoteRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt index 503575b69..cd78abd3c 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 @@ -18,7 +18,11 @@ class NoteRemote @Inject constructor(private val sdk: Sdk) { 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/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..8a38fa557 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 @@ -1,10 +1,12 @@ package io.github.wulkanowy.services.sync import android.content.Context +import android.os.Build.VERSION_CODES.LOLLIPOP 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 @@ -17,6 +19,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.sdk.exception.FeatureDisabledException import io.github.wulkanowy.services.sync.channels.DebugChannel import io.github.wulkanowy.services.sync.works.Work +import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.getCompatColor import io.reactivex.Completable import io.reactivex.Single @@ -30,7 +33,8 @@ class SyncWorker @AssistedInject constructor( private val semesterRepository: SemesterRepository, private val works: Set<@JvmSuppressWildcards Work>, private val preferencesRepository: PreferencesRepository, - private val notificationManager: NotificationManagerCompat + private val notificationManager: NotificationManagerCompat, + private val appInfo: AppInfo ) : RxWorker(appContext, workerParameters) { override fun createWork(): Single { @@ -52,8 +56,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 { + it is FeatureDisabledException -> Result.success() + 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 +75,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/ui/base/ErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt index ba10af6bf..34dc3418b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt @@ -1,7 +1,7 @@ package io.github.wulkanowy.ui.base import android.content.res.Resources -import com.readystatesoftware.chuck.api.ChuckCollector +import com.chuckerteam.chucker.api.ChuckerCollector import io.github.wulkanowy.R import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.github.wulkanowy.sdk.exception.BadCredentialsException @@ -15,7 +15,7 @@ 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,7 +24,7 @@ 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) } 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/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..26d16b1df 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,6 +95,10 @@ 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 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..58622848e --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsDialog.kt @@ -0,0 +1,79 @@ +package io.github.wulkanowy.ui.modules.homework.details + +import android.annotation.SuppressLint +import android.os.Bundle +import android.text.method.LinkMovementMethod +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.text.HtmlCompat +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.toFormattedString +import kotlinx.android.synthetic.main.dialog_homework.* +import javax.inject.Inject + +class HomeworkDetailsDialog : BaseDialogFragment(), HomeworkDetailsView { + + @Inject + lateinit var presenter: HomeworkDetailsPresenter + + 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() { + homeworkDialogDate.text = homework.date.toFormattedString() + homeworkDialogEntryDate.text = homework.entryDate.toFormattedString() + homeworkDialogSubject.text = homework.subject + homeworkDialogTeacher.text = homework.teacher + homeworkDialogContent.movementMethod = LinkMovementMethod.getInstance() + homeworkDialogContent.text = homework.content + homeworkDialogAttachments.movementMethod = LinkMovementMethod.getInstance() + homeworkDialogAttachments.text = HtmlCompat.fromHtml(homework.attachments.joinToString("
") { + "${it.second}" + }, HtmlCompat.FROM_HTML_MODE_COMPACT).ifBlank { getString(R.string.all_no_data) } + + 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() } + } + + 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..999ec22ab 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 @@ -121,6 +121,20 @@ class LoginFormFragment : BaseFragment(), LoginFormView { } } + 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 setErrorSymbolRequired(focus: Boolean) { with(loginFormSymbolLayout) { if (focus) requestFocus() @@ -219,12 +233,18 @@ class LoginFormFragment : BaseFragment(), LoginFormView { } } - 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/$formSymbolValue", + 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..e566d3d6b 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 { @@ -109,6 +111,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,7 +121,7 @@ class LoginFormPresenter @Inject constructor( } fun onEmailClick() { - view?.openEmail() + view?.openEmail(lastError?.message.ifNullOrBlank { "none" }) } fun onRecoverClick() { @@ -131,6 +134,16 @@ class LoginFormPresenter @Inject constructor( 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()) { 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..8e46f93fd 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 @@ -31,6 +31,10 @@ interface LoginFormView : BaseView { fun setErrorUsernameRequired() + fun setErrorLoginRequired() + + fun setErrorEmailRequired() + fun setErrorSymbolRequired(focus: Boolean) fun setErrorPassRequired(focus: Boolean) @@ -63,7 +67,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/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..e87e260f7 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 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..3f52be895 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) { @@ -160,7 +151,7 @@ class MessagePreviewFragment : BaseFragment(), MessagePreviewView, MainView.Titl override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) - outState.putLong(MESSAGE_ID_KEY, presenter.messageId) + outState.putSerializable(MESSAGE_ID_KEY, presenter.message) } 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..7bf9c05e7 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,51 @@ 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) + loadData(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) } + .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) }) } @@ -119,8 +107,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/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/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/settings/SettingsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/SettingsFragment.kt index 03a89d274..5a8593a06 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,19 @@ 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) { + 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 +108,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..ce5100ea8 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,25 @@ class SettingsPresenter @Inject constructor( } analytics.logEvent("setting_changed", "name" to key) } + + fun onSyncNowClicked() { + view?.showForceSyncDialog() + } + + fun onForceSyncDialogSubmit() { + view?.run { + Timber.i("Setting sync now started") + analytics.logEvent("sync_now_started") + disposable.add(syncManager.startOneTimeSyncWorker() + .doOnSubscribe { setSyncInProgress(true) } + .doFinally { setSyncInProgress(false) } + .subscribe({ workInfo -> + if (workInfo.state == WorkInfo.State.SUCCEEDED) showMessage(syncSuccessString) + else if (workInfo.state == WorkInfo.State.FAILED) showError(syncFailedString, Throwable(workInfo.outputData.getString("error"))) + }, { + Timber.e("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/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/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/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index be1a02e7f..64aa622c8 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,11 @@ -Wersja 0.16.0 +Wersja 0.17.0 -- 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 +- dodaliśmy wsparcie dla załączników w wiadomosciach i zadaniach domowych +- dodaliśmy oznaczanie zadań domowych jako wykonanych +- dodaliśmy wyświetlanie punktów przy uwagach +- dodaliśmy funkcję powiadomień o awariach dziennika +- dodaliśmy funkcję wymuszenia pełnej synchronizacji +- zmieniliśmy sposób zwracania się do użytkownika na bezosobowy +- naprawiliśmy logowanie na androidach niższych niż 5.0 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_homework.xml b/app/src/main/res/layout/dialog_homework.xml index 7ed9b795b..f6d000495 100644 --- a/app/src/main/res/layout/dialog_homework.xml +++ b/app/src/main/res/layout/dialog_homework.xml @@ -1,5 +1,6 @@ @@ -97,13 +98,46 @@ android:textIsSelectable="true" android:textSize="12sp" /> - + android:layout_marginTop="10dp" + android:text="@string/homework_attachments" + android:textSize="17sp" /> + + + + + + + + + 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" /> + + + + + + + app:layout_constraintTop_toBottomOf="@+id/loginSymbolHelper"> - - - - - - - - - - - - - - + android:layout_height="match_parent" + tools:itemCount="1" + tools:listitem="@layout/item_message_preview" /> + android:ellipsize="end" + android:singleLine="true" + android:textSize="16sp" + app:layout_constraintEnd_toStartOf="@+id/homeworkItemTeacher" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="@tools:sample/lorem/random" /> - + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="@id/homeworkItemSubject" + app:layout_constraintTop_toBottomOf="@id/homeworkItemSubject" + tools:text="@tools:sample/lorem/random" /> + + + + + diff --git a/app/src/main/res/layout/item_license.xml b/app/src/main/res/layout/item_license.xml index 6a864433a..f44c211ca 100644 --- a/app/src/main/res/layout/item_license.xml +++ b/app/src/main/res/layout/item_license.xml @@ -14,9 +14,11 @@ android:id="@+id/licenseItemName" android:layout_width="match_parent" android:layout_height="28dp" + android:ellipsize="end" android:gravity="bottom" + android:singleLine="true" android:textSize="16sp" - tools:text="@tools:sample/lorem" /> + tools:text="@tools:sample/lorem/random" /> + app:layout_constraintEnd_toStartOf="@+id/messageItemDate" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="@tools:sample/lorem/random" /> - + + + diff --git a/app/src/main/res/layout/item_message_attachment.xml b/app/src/main/res/layout/item_message_attachment.xml new file mode 100644 index 000000000..fa30e9af2 --- /dev/null +++ b/app/src/main/res/layout/item_message_attachment.xml @@ -0,0 +1,22 @@ + + + + diff --git a/app/src/main/res/layout/item_message_divider.xml b/app/src/main/res/layout/item_message_divider.xml new file mode 100644 index 000000000..2f4ad3e67 --- /dev/null +++ b/app/src/main/res/layout/item_message_divider.xml @@ -0,0 +1,6 @@ + diff --git a/app/src/main/res/layout/item_message_preview.xml b/app/src/main/res/layout/item_message_preview.xml new file mode 100644 index 000000000..6c130909d --- /dev/null +++ b/app/src/main/res/layout/item_message_preview.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_note.xml b/app/src/main/res/layout/item_note.xml index a5d3e3623..3a56f9de7 100644 --- a/app/src/main/res/layout/item_note.xml +++ b/app/src/main/res/layout/item_note.xml @@ -1,4 +1,5 @@ - + + - + diff --git a/app/src/main/res/menu/action_menu_logviewer.xml b/app/src/main/res/menu/action_menu_logviewer.xml index cb2b31a0f..84b076b15 100644 --- a/app/src/main/res/menu/action_menu_logviewer.xml +++ b/app/src/main/res/menu/action_menu_logviewer.xml @@ -3,7 +3,7 @@ Ungültige token
Token ist nicht mehr gültig Ungültige email + Ungültige login Ungültige symbol Student nicht gefunden. Überprüfen Sie das Symbol Dieses Datenfeld ist erforderlich Ausgewählter Student ist bereits angemeldet. - Das Symbol finden Sie auf der Registerseite unter Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne + Das Symbol finden Sie auf der Registerseite unter Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne Wählen Sie die Studenten aus, die sich bei der Anwendung anmelden sollen. Andere Optionen Datenschutzerklärung @@ -201,6 +202,7 @@ Keine Informationen über Eintragen + Punkte %d Eintrag %d Eintragen @@ -217,6 +219,9 @@ Keine Informationen über Hausaufgaben + Gemacht + Unvollständig + Anhänge @@ -354,6 +359,7 @@ Glückliche Nummer Neue Nachrichten Neue Eintragen + Push-Benachrichtigungen Debuggen diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 041a65785..9cd652723 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -44,16 +44,17 @@ Klucz API Zaloguj To hasło jest za krótkie - Dane logowania są niepoprawne. Upewnij się, że został wybrany odpowiedni dziennik UONET+ + Dane logowania są niepoprawne. Upewnij się, że został wybrany odpowiedni dziennik UONET+ w polu poniżej Nieprawidłowy PIN Nieprawidłowy token Token stracił ważność Niepoprawny adres email + Niepoprawny login Niepoprawny symbol Nie znaleziono ucznia. Sprawdź symbol To pole jest wymagane Wybrany uczeń jest już zalogowany - Symbol znajdziesz na stronie dziennika w Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne + Symbol znajdziesz na stronie dziennika w Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne Wybierz uczniów do zalogowania w aplikacji Inne opcje W tym trybie nie działa szczęśliwy numerek, uczeń na tle klasy, podsumowanie frekwencji, usprawiedliwianie nieobecności, lekcje zrealizowane, informacje o szkole i podgląd listy zarejestrowanych urządzeń @@ -117,10 +118,10 @@ Nowe oceny - Dostałeś %1$d ocenę - "Dostałeś %1$d oceny - Dostałeś %1$d ocen - Dostałeś %1$d ocen + Masz %1$d nową ocenę + Masz %1$d nowe oceny + Masz %1$d nowych ocen + Masz %1$d nowych ocen @@ -144,13 +145,13 @@ Podsumowanie frekwencji - Nieobecny z przyczyn szkolnych + Nieobecność z przyczyn szkolnych Nieobecność usprawiedliwiona Nieobecność nieusprawiedliwiona - Zwolniony + Zwolnienie Spóźnienie usprawiedliwione Spóźnienie nieusprawiedliwione - Obecny + Obecność Numer lekcji Brak wpisów @@ -211,15 +212,16 @@ Nowe wiadomości - Dostałeś %1$d wiadomość - "Dostałeś %1$d wiadomości - Dostałeś %1$d wiadomości - Dostałeś %1$d wiadomości + Masz %1$d nową wiadomość + Masz %1$d nowe wiadomości + Masz %1$d nowych wiadomości + Masz %1$d nowych wiadomości Brak informacji o uwagach + Punkty %d uwaga %d uwagi @@ -233,15 +235,18 @@ Nowe uwagi - Dostałeś %1$d uwagę - "Dostałeś %1$d uwagi - Dostałeś %1$d uwag - Dostałeś %1$d uwag + Masz %1$d nową uwagę + Masz %1$d nowe uwagi + Masz %1$d nowych uwag + Masz %1$d nowych uwag Brak zadań domowych + Wykonane + Niewykonane + Załączniki @@ -369,6 +374,15 @@ Zawieszona na wakacjach Interwał aktualizacji Tylko WiFi + Synchronizuj teraz + Zsynchronizowano! + Synchronizacja nie powiodła się + Synchronizacja w trakcie + Synchronizacja + + Ręczna synchronizacja nie odświeża widoków w aplikacji. + \nAby zobaczyć zsynchronizowane informacje uruchom ponownie aplikację po zsynchronizowaniu. + Inne Wartość plusa @@ -382,6 +396,7 @@ Nowe oceny Nowe wiadomości Nowe uwagi + Powiadomienia push Debugowanie diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6134d755a..e637e3ea9 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -49,11 +49,12 @@ Недействительный token Токен просрочен Неверный адрес электронной почты + Неправильный логин Недействительный symbol Не удалось найти ученика. Пожалуйста, проверьте \"symbol\" Это поле обязательно Данный ученик уже авторизован - Вы можете найти \"symbol\" в Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne + Вы можете найти \"symbol\" в Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne Выберите учеников для авторизации в приложении Другие варианты Политика приватности @@ -216,6 +217,7 @@ Нет данных о предупреждениях + точек %d предупреждение %d предупреждения @@ -238,6 +240,9 @@ Нет домашних заданий + сделанный + Не сделано + Вложения @@ -379,6 +384,7 @@ Счастливый номер Новые сообщения Новые заметки + Push-уведомления Дебаг diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 9b43f4a2c..9caf13a6b 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -49,12 +49,13 @@ Недійсний PIN Недійсний token Недійсна адреса електронної пошти + Невірний логін Токен протермінований Недійсний symbol Не вдалося знайти учня. Будь ласка, перевірте \"symbol\" Це поле обов\'язкове Даний учень вже авторизований - Ви можете знайти \"symbol\" в Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne + Ви можете знайти \"symbol\" в Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne Виберіть учнів для авторизації в додатку Інші варіанти Політика приватності @@ -217,6 +218,7 @@ Немає даних про нотатки + точок %d нотатка %d нотатки @@ -239,6 +241,9 @@ Немає домашніх завдань + зроблений + Не зроблено + Вкладення @@ -380,6 +385,7 @@ Нові оцінки Нові повідомлення Нові нотатки + Повідомлення pushs Дебаг diff --git a/app/src/main/res/values/api_hosts.xml b/app/src/main/res/values/api_hosts.xml index 71162dc10..0e6b2e9cd 100644 --- a/app/src/main/res/values/api_hosts.xml +++ b/app/src/main/res/values/api_hosts.xml @@ -28,6 +28,6 @@ lublin tarnow rzeszow - Default + powiatwulkanowy diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 6221ef99b..36dee1812 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -12,6 +12,9 @@ #1f000000 #1fffffff + #a0c431 + #d43f3f + #424242 #d43f3f #49a6f2 diff --git a/app/src/main/res/values/library_wulkanowy_api.xml b/app/src/main/res/values/library_wulkanowy_api.xml deleted file mode 100644 index d16bf86bd..000000000 --- a/app/src/main/res/values/library_wulkanowy_api.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Wulkanowy - https://github.com/wulkanowy - - UONET+ Scraping API - The UONET+ client using web scraping - https://github.com/wulkanowy/api - Development - - true - https://github.com/wulkanowy/api - - apache_2_0 - diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml index 49601b550..559e2159c 100644 --- a/app/src/main/res/values/preferences_keys.xml +++ b/app/src/main/res/values/preferences_keys.xml @@ -11,6 +11,7 @@ services_enable services_interval services_disable_wifi_only + services_force_sync notifications_enable notification_debug grade_modifier_plus diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1e98ba946..ae1627ae5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -50,11 +50,12 @@ Invalid token Token expired Invalid email + Invalid login Invalid symbol Student not found. Check the symbol This field is required Selected student is already logged in - The symbol can be found on the register page in Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne + The symbol can be found on the register page in Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne Select students to log in to the application Other options In this mode, a lucky number does not work, a class grade stats, summary of attendance, excuse for absence, completed lessons, school information and preview of the list of registered devices @@ -65,8 +66,9 @@ Email Discord Send email + Describe details of problem: Zgłoszenie: Problemy z logowaniem - Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\n\nOpis problemu: + Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nDodatkowe informacje: %4$s\nOstatni błąd: %5$s\n\nOpis problemu: Make sure the correct UONET+ register is selected! I forgot my password Recover your account @@ -209,6 +211,7 @@ No info about notes + Points %d note %d notes @@ -225,6 +228,9 @@ No info about homework + Mark as done + Mark as undone + Attachments @@ -290,6 +296,7 @@ Visit the website and help develop the application Licenses Licenses of libraries used in the application + Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\n\nTreść zgłoszenia: @@ -352,6 +359,15 @@ Suspended on holidays Updates interval Wi-Fi only + Sync now + Synced! + Sync failed + Sync in progress + Synchronization + + Manual sync doesn\'t refresh app views. + \nTo see the synced data relaunch the app after syncing. + Other Value of the plus @@ -365,6 +381,7 @@ Lucky number New messages New notes + Push notifications Debug diff --git a/app/src/main/res/xml/scheme_preferences.xml b/app/src/main/res/xml/scheme_preferences.xml index e92aca63f..bb9eb5fcd 100644 --- a/app/src/main/res/xml/scheme_preferences.xml +++ b/app/src/main/res/xml/scheme_preferences.xml @@ -77,6 +77,10 @@ app:iconSpaceReserved="false" app:key="@string/pref_key_services_wifi_only" app:title="@string/pref_services_wifi" /> + () + val message = repo.getMessage(student, testMessage) + val messageObserver = TestObserver() message.subscribe(messageObserver) messageObserver.assertError(EmptyResultSetException::class.java) } @Test fun `get message when content already in db`() { - `when`(local.getMessage(123)).thenReturn(Single.just( - Message(1, 1, 123, "", 1, "", "", "Test", now(), 1, false, 1, 1, false) - )) + val testMessage = Message(1, 1, 123, "", 1, "", "", "Test", now(), 1, false, 1, 1, false, false) + val messageWithAttachment = MessageWithAttachment(testMessage, emptyList()) - val message = repo.getMessage(student, 123).blockingGet() + `when`(local.getMessageWithAttachment(student, testMessage)).thenReturn(Single.just(messageWithAttachment)) - assertEquals("Test", message.content) + val message = repo.getMessage(student, testMessage).blockingGet() + + assertEquals("Test", message.message.content) } @Test fun `get message when content in db is empty`() { - val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, true, 1, 1, false) + val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, true, 1, 1, false, false) val testMessageWithContent = testMessage.copy(content = "Test") - `when`(local.getMessage(123)) - .thenReturn(Single.just(testMessage)) - .thenReturn(Single.just(testMessageWithContent)) - `when`(remote.getMessagesContent(testMessageWithContent)).thenReturn(Single.just("Test")) + val mWa = MessageWithAttachment(testMessage, emptyList()) + val mWaWithContent = MessageWithAttachment(testMessageWithContent, emptyList()) - val message = repo.getMessage(student, 123).blockingGet() + `when`(local.getMessageWithAttachment(student, testMessage)) + .thenReturn(Single.just(mWa)) + .thenReturn(Single.just(mWaWithContent)) + `when`(remote.getMessagesContentDetails(testMessageWithContent)).thenReturn(Single.just("Test" to emptyList())) - assertEquals("Test", message.content) + val message = repo.getMessage(student, testMessage).blockingGet() + + assertEquals("Test", message.message.content) verify(local).updateMessages(listOf(testMessageWithContent)) } @Test fun `get message when content in db is empty and there is no internet connection`() { - val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, false, 1, 1, false) + val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, false, 1, 1, false, false) + val messageWithAttachment = MessageWithAttachment(testMessage, emptyList()) testObservingStrategy.isInternetConnection = false - `when`(local.getMessage(123)).thenReturn(Single.just(testMessage)) + `when`(local.getMessageWithAttachment(student, testMessage)).thenReturn(Single.just(messageWithAttachment)) - val message = repo.getMessage(student, 123) - val messageObserver = TestObserver() + val message = repo.getMessage(student, testMessage) + val messageObserver = TestObserver() + message.subscribe(messageObserver) + messageObserver.assertError(UnknownHostException::class.java) + } + + @Test + fun `get message when content in db is empty, unread and there is no internet connection`() { + val testMessage = Message(1, 1, 123, "", 1, "", "", "", now(), 1, true, 1, 1, false, false) + val messageWithAttachment = MessageWithAttachment(testMessage, emptyList()) + + testObservingStrategy.isInternetConnection = false + `when`(local.getMessageWithAttachment(student, testMessage)).thenReturn(Single.just(messageWithAttachment)) + + val message = repo.getMessage(student, testMessage) + val messageObserver = TestObserver() message.subscribe(messageObserver) messageObserver.assertError(UnknownHostException::class.java) } diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt index 05b543f10..779a605fc 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt @@ -52,7 +52,7 @@ class LoginFormPresenterTest { fun emptyNicknameLoginTest() { `when`(loginFormView.formUsernameValue).thenReturn("") `when`(loginFormView.formPassValue).thenReturn("test123") - `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") + `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf/?standard") presenter.onSignInClick() verify(loginFormView).setErrorUsernameRequired() @@ -64,7 +64,7 @@ class LoginFormPresenterTest { fun emptyPassLoginTest() { `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("") - `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") + `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf/?standard") presenter.onSignInClick() verify(loginFormView, never()).setErrorUsernameRequired() @@ -76,7 +76,7 @@ class LoginFormPresenterTest { fun invalidPassLoginTest() { `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123") - `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") + `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf/?standard") presenter.onSignInClick() verify(loginFormView, never()).setErrorUsernameRequired() @@ -86,12 +86,12 @@ class LoginFormPresenterTest { @Test fun loginTest() { - val studentTest = Student(email = "test@", password = "123", scrapperBaseUrl = "https://fakelog.cf", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, classId = 1, isCurrent = false, symbol = "", registrationDate = now(), className = "", mobileBaseUrl = "", privateKey = "", certificateKey = "", loginMode = "", userLoginId = 0, schoolShortName = "", isParent = false) + val studentTest = Student(email = "test@", password = "123", scrapperBaseUrl = "https://fakelog.cf/", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, classId = 1, isCurrent = false, symbol = "", registrationDate = now(), className = "", mobileBaseUrl = "", privateKey = "", certificateKey = "", loginMode = "", userLoginId = 0, schoolShortName = "", isParent = false) doReturn(Single.just(listOf(studentTest))).`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") - `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") + `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf/?standard") `when`(loginFormView.formSymbolValue).thenReturn("Default") `when`(loginFormView.formHostSymbol).thenReturn("Default") presenter.onSignInClick() @@ -109,7 +109,7 @@ class LoginFormPresenterTest { .`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") - `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") + `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf/?standard") `when`(loginFormView.formSymbolValue).thenReturn("Default") `when`(loginFormView.formHostSymbol).thenReturn("Default") presenter.onSignInClick() @@ -127,7 +127,7 @@ class LoginFormPresenterTest { .`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") - `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") + `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf/?standard") `when`(loginFormView.formSymbolValue).thenReturn("Default") `when`(loginFormView.formHostSymbol).thenReturn("Default") presenter.onSignInClick() @@ -146,7 +146,7 @@ class LoginFormPresenterTest { doReturn(Single.error>(testException)).`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) `when`(loginFormView.formUsernameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") - `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") + `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf/?standard") `when`(loginFormView.formSymbolValue).thenReturn("Default") `when`(loginFormView.formHostSymbol).thenReturn("Default") presenter.onSignInClick() diff --git a/build.gradle b/build.gradle index 52c7626ec..47a365b6c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ buildscript { - ext.kotlin_version = '1.3.70' + ext.kotlin_version = '1.3.71' + ext.about_libraries = '8.1.0' repositories { mavenCentral() google() @@ -9,12 +10,14 @@ buildscript { } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.android.tools.build:gradle:3.6.1' + classpath 'com.android.tools.build:gradle:3.6.2' classpath 'com.google.gms:google-services:4.3.3' - classpath "io.fabric.tools:gradle:1.31.2" - classpath "com.github.triplet.gradle:play-publisher:2.4.2" - classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8.0.1969" + //noinspection GradleDependency https://github.com/firebase/firebase-android-sdk/issues/1276#issuecomment-592098283 + classpath "io.fabric.tools:gradle:1.31.0" + classpath "com.github.triplet.gradle:play-publisher:2.7.3" + classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8" classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0" + classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${about_libraries}" } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf01..f3d88b1c2 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6254d2d4a..84a906615 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 83f2acfdc..2fe81a7d9 100755 --- a/gradlew +++ b/gradlew @@ -154,19 +154,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -175,14 +175,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 24467a141..9109989e3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"