diff --git a/.travis.yml b/.travis.yml index fa81da66..80628cc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ cache: branches: only: - develop - - 0.14.2 + - 0.15.0 android: licenses: diff --git a/app/build.gradle b/app/build.gradle index 24a470ae..24306cd1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 16 targetSdkVersion 29 - versionCode 51 - versionName "0.14.2" + versionCode 52 + versionName "0.15.0" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -110,7 +110,7 @@ play { } ext { - work_manager = "2.3.0-rc01" + work_manager = "2.3.0" room = "2.2.3" dagger = "2.25.4" chucker = "2.0.4" @@ -122,14 +122,14 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:0.14.2" + implementation "io.github.wulkanowy:sdk:0.15.0" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0-rc01" - implementation "androidx.activity:activity-ktx:1.1.0-rc03" + implementation "androidx.activity:activity-ktx:1.1.0" implementation "androidx.appcompat:appcompat:1.1.0" implementation "androidx.appcompat:appcompat-resources:1.1.0" - implementation "androidx.fragment:fragment-ktx:1.2.0-rc05" + implementation "androidx.fragment:fragment-ktx:1.2.0" implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.multidex:multidex:2.0.1" @@ -139,7 +139,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03" implementation "androidx.constraintlayout:constraintlayout:1.1.3" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" - implementation "com.google.android.material:material:1.1.0-rc01" + implementation "com.google.android.material:material:1.1.0-rc02" implementation "com.github.wulkanowy:material-chips-input:2.0.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "me.zhanghai.android.materialprogressbar:library:1.6.1" @@ -167,16 +167,18 @@ dependencies { implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6" implementation "io.reactivex.rxjava2:rxandroid:2.1.1" - implementation "io.reactivex.rxjava2:rxjava:2.2.16" + implementation "io.reactivex.rxjava2:rxjava:2.2.17" implementation "com.google.code.gson:gson:2.8.6" - implementation "com.jakewharton.threetenabp:threetenabp:1.2.1" + implementation "com.jakewharton.threetenabp:threetenabp:1.2.2" implementation "com.jakewharton.timber:timber:4.7.1" implementation "at.favre.lib:slf4j-timber:1.0.1" implementation "com.mikepenz:aboutlibraries-core:7.1.0" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' - playImplementation "com.google.firebase:firebase-core:17.2.1" + implementation("io.coil-kt:coil:0.9.2") + + playImplementation "com.google.firebase:firebase-core:17.2.2" playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1" releaseImplementation "fr.o80.chucker:library-no-op:$chucker" @@ -186,7 +188,7 @@ dependencies { testImplementation "junit:junit:4.13" testImplementation "io.mockk:mockk:$mockk" - testImplementation "org.threeten:threetenbp:1.4.0" + testImplementation "org.threeten:threetenbp:1.4.1" testImplementation "org.mockito:mockito-inline:3.2.4" androidTestImplementation "androidx.test:core:1.2.0" diff --git a/app/jacoco.gradle b/app/jacoco.gradle index 48a8fed9..e9abfb61 100644 --- a/app/jacoco.gradle +++ b/app/jacoco.gradle @@ -1,7 +1,7 @@ apply plugin: "jacoco" jacoco { - toolVersion "0.8.4" + toolVersion "0.8.5" reportsDir = file("$buildDir/reports") } diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json new file mode 100644 index 00000000..925d787a --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json @@ -0,0 +1,1634 @@ +{ + "formatVersion": 1, + "database": { + "version": 20, + "identityHash": "37a216a7afcea922b66001ca5ed6cf74", + "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_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": "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, `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, `is_current` 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": "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": "isCurrent", + "columnName": "is_current", + "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": "studentPlan", + "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, `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)", + "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": "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 + } + ], + "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, `category` TEXT 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": "category", + "columnName": "category", + "affinity": "TEXT", + "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, '37a216a7afcea922b66001ca5ed6cf74')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json new file mode 100644 index 00000000..dfad69f5 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json @@ -0,0 +1,1652 @@ +{ + "formatVersion": 1, + "database": { + "version": 21, + "identityHash": "f905d8a3ff4718d30ae7413a5a717b1f", + "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_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": "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, `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, `is_current` 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": "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": "isCurrent", + "columnName": "is_current", + "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": "studentPlan", + "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, `category` TEXT 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": "category", + "columnName": "category", + "affinity": "TEXT", + "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, 'f905d8a3ff4718d30ae7413a5a717b1f')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt index 69502e74..5fd5b193 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt @@ -35,9 +35,9 @@ class AttendanceLocalTest { @Test fun saveAndReadTest() { attendanceLocal.saveAttendance(listOf( - Attendance(1, 2, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false), - Attendance(1, 2, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false), - Attendance(1, 2, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false) + Attendance(1, 2, 3, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name), + Attendance(1, 2, 3, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name), + Attendance(1, 2, 3, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name) )) val attendance = attendanceLocal diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt index e2ce2255..77c06182 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt @@ -2,8 +2,8 @@ package io.github.wulkanowy.data.repositories.timetable import org.threeten.bp.LocalDateTime import org.threeten.bp.LocalDateTime.now -import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal +import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal { return TimetableLocal( @@ -21,6 +21,7 @@ fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", s teacher = teacher, teacherOld = "", info = "", + studentPlan = true, changes = changes, canceled = false ) diff --git a/app/src/debug/res/drawable-anydpi-v24/ic_stat_grade.xml b/app/src/debug/res/drawable-anydpi-v24/ic_stat_grade.xml new file mode 100644 index 00000000..832eba83 --- /dev/null +++ b/app/src/debug/res/drawable-anydpi-v24/ic_stat_grade.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable-anydpi-v24/ic_stat_luckynumber.xml b/app/src/debug/res/drawable-anydpi-v24/ic_stat_luckynumber.xml new file mode 100644 index 00000000..4f3eb98e --- /dev/null +++ b/app/src/debug/res/drawable-anydpi-v24/ic_stat_luckynumber.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable-anydpi-v24/ic_stat_message.xml b/app/src/debug/res/drawable-anydpi-v24/ic_stat_message.xml new file mode 100644 index 00000000..8fe12de4 --- /dev/null +++ b/app/src/debug/res/drawable-anydpi-v24/ic_stat_message.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable-anydpi-v24/ic_stat_note.xml b/app/src/debug/res/drawable-anydpi-v24/ic_stat_note.xml new file mode 100644 index 00000000..d30f2233 --- /dev/null +++ b/app/src/debug/res/drawable-anydpi-v24/ic_stat_note.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable-hdpi/ic_stat_grade.png b/app/src/debug/res/drawable-hdpi/ic_stat_grade.png new file mode 100644 index 00000000..013b7ac4 Binary files /dev/null and b/app/src/debug/res/drawable-hdpi/ic_stat_grade.png differ diff --git a/app/src/debug/res/drawable-hdpi/ic_stat_luckynumber.png b/app/src/debug/res/drawable-hdpi/ic_stat_luckynumber.png new file mode 100644 index 00000000..74a4d0c5 Binary files /dev/null and b/app/src/debug/res/drawable-hdpi/ic_stat_luckynumber.png differ diff --git a/app/src/debug/res/drawable-hdpi/ic_stat_message.png b/app/src/debug/res/drawable-hdpi/ic_stat_message.png new file mode 100644 index 00000000..be41e343 Binary files /dev/null and b/app/src/debug/res/drawable-hdpi/ic_stat_message.png differ diff --git a/app/src/debug/res/drawable-hdpi/ic_stat_note.png b/app/src/debug/res/drawable-hdpi/ic_stat_note.png new file mode 100644 index 00000000..1992edf2 Binary files /dev/null and b/app/src/debug/res/drawable-hdpi/ic_stat_note.png differ diff --git a/app/src/debug/res/drawable-mdpi/ic_stat_grade.png b/app/src/debug/res/drawable-mdpi/ic_stat_grade.png new file mode 100644 index 00000000..a5df2a35 Binary files /dev/null and b/app/src/debug/res/drawable-mdpi/ic_stat_grade.png differ diff --git a/app/src/debug/res/drawable-mdpi/ic_stat_luckynumber.png b/app/src/debug/res/drawable-mdpi/ic_stat_luckynumber.png new file mode 100644 index 00000000..278ed2c6 Binary files /dev/null and b/app/src/debug/res/drawable-mdpi/ic_stat_luckynumber.png differ diff --git a/app/src/debug/res/drawable-mdpi/ic_stat_message.png b/app/src/debug/res/drawable-mdpi/ic_stat_message.png new file mode 100644 index 00000000..7327a02f Binary files /dev/null and b/app/src/debug/res/drawable-mdpi/ic_stat_message.png differ diff --git a/app/src/debug/res/drawable-mdpi/ic_stat_note.png b/app/src/debug/res/drawable-mdpi/ic_stat_note.png new file mode 100644 index 00000000..2fb02098 Binary files /dev/null and b/app/src/debug/res/drawable-mdpi/ic_stat_note.png differ diff --git a/app/src/debug/res/drawable-xhdpi/ic_stat_grade.png b/app/src/debug/res/drawable-xhdpi/ic_stat_grade.png new file mode 100644 index 00000000..c63f810f Binary files /dev/null and b/app/src/debug/res/drawable-xhdpi/ic_stat_grade.png differ diff --git a/app/src/debug/res/drawable-xhdpi/ic_stat_luckynumber.png b/app/src/debug/res/drawable-xhdpi/ic_stat_luckynumber.png new file mode 100644 index 00000000..4035ceba Binary files /dev/null and b/app/src/debug/res/drawable-xhdpi/ic_stat_luckynumber.png differ diff --git a/app/src/debug/res/drawable-xhdpi/ic_stat_message.png b/app/src/debug/res/drawable-xhdpi/ic_stat_message.png new file mode 100644 index 00000000..c4140be8 Binary files /dev/null and b/app/src/debug/res/drawable-xhdpi/ic_stat_message.png differ diff --git a/app/src/debug/res/drawable-xhdpi/ic_stat_note.png b/app/src/debug/res/drawable-xhdpi/ic_stat_note.png new file mode 100644 index 00000000..6b533c8e Binary files /dev/null and b/app/src/debug/res/drawable-xhdpi/ic_stat_note.png differ diff --git a/app/src/debug/res/drawable-xxhdpi/ic_stat_grade.png b/app/src/debug/res/drawable-xxhdpi/ic_stat_grade.png new file mode 100644 index 00000000..13c26b77 Binary files /dev/null and b/app/src/debug/res/drawable-xxhdpi/ic_stat_grade.png differ diff --git a/app/src/debug/res/drawable-xxhdpi/ic_stat_luckynumber.png b/app/src/debug/res/drawable-xxhdpi/ic_stat_luckynumber.png new file mode 100644 index 00000000..da435745 Binary files /dev/null and b/app/src/debug/res/drawable-xxhdpi/ic_stat_luckynumber.png differ diff --git a/app/src/debug/res/drawable-xxhdpi/ic_stat_message.png b/app/src/debug/res/drawable-xxhdpi/ic_stat_message.png new file mode 100644 index 00000000..9d0fa781 Binary files /dev/null and b/app/src/debug/res/drawable-xxhdpi/ic_stat_message.png differ diff --git a/app/src/debug/res/drawable-xxhdpi/ic_stat_note.png b/app/src/debug/res/drawable-xxhdpi/ic_stat_note.png new file mode 100644 index 00000000..64da443f Binary files /dev/null and b/app/src/debug/res/drawable-xxhdpi/ic_stat_note.png differ diff --git a/app/src/main/assets/creators.json b/app/src/main/assets/creators.json new file mode 100644 index 00000000..b99f51e2 --- /dev/null +++ b/app/src/main/assets/creators.json @@ -0,0 +1,34 @@ +[ + { + "displayName": "Mikołaj Pich", + "githubUsername": "mklkj" + }, + { + "displayName": "Rafał Borcz", + "githubUsername": "Faierbel" + }, + { + "displayName": "Dominik Korsa", + "githubUsername": "dominik-korsa" + }, + { + "displayName": "Kacper Ziubryniewicz", + "githubUsername": "kapi2289" + }, + { + "displayName": "doteq", + "githubUsername": "doteq" + }, + { + "displayName": "Pavuloff", + "githubUsername": "pavuloff" + }, + { + "displayName": "Piotr Romanowski", + "githubUsername": "v0idzz" + }, + { + "displayName": "Dinolek", + "githubUsername": "Dinolek" + } +] 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 1bc4eb61..48e51e3c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.data import android.content.Context import android.content.SharedPreferences +import android.content.res.AssetManager import android.content.res.Resources import androidx.preference.PreferenceManager import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings @@ -58,6 +59,10 @@ internal class RepositoryModule { @Provides fun provideResources(context: Context): Resources = context.resources + @Singleton + @Provides + fun provideAssets(context: Context): AssetManager = context.assets + @Singleton @Provides fun provideSharedPref(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 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 9ee1b263..b5293a53 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 @@ -60,6 +60,8 @@ import io.github.wulkanowy.data.db.migrations.Migration17 import io.github.wulkanowy.data.db.migrations.Migration18 import io.github.wulkanowy.data.db.migrations.Migration19 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.Migration3 import io.github.wulkanowy.data.db.migrations.Migration4 import io.github.wulkanowy.data.db.migrations.Migration5 @@ -101,7 +103,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 19 + const val VERSION_SCHEMA = 21 fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array { return arrayOf( @@ -122,7 +124,9 @@ abstract class AppDatabase : RoomDatabase() { Migration16(), Migration17(), Migration18(), - Migration19(sharedPrefProvider) + Migration19(sharedPrefProvider), + Migration20(), + Migration21() ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt index 3c58971a..aa8da8db 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt @@ -15,6 +15,9 @@ data class Attendance( @ColumnInfo(name = "diary_id") val diaryId: Int, + @ColumnInfo(name = "time_id") + val timeId: Int, + val date: LocalDate, val number: Int, @@ -33,7 +36,13 @@ data class Attendance( val excused: Boolean, - val deleted: Boolean + val deleted: Boolean, + + val excusable: Boolean, + + @ColumnInfo(name = "excuse_status") + val excuseStatus: String? + ) : Serializable { @PrimaryKey(autoGenerate = true) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt index 9bc3d214..313feb62 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt @@ -40,6 +40,9 @@ data class Timetable( val info: String, + @ColumnInfo(name = "student_plan") + val studentPlan: Boolean, + val changes: Boolean, val canceled: Boolean diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt new file mode 100644 index 00000000..2fcfc183 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration20 : Migration(19, 20) { + + override fun migrate(database: SupportSQLiteDatabase) { + migrateTimetable(database) + truncateSubjects(database) + } + + private fun migrateTimetable(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE Timetable") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS `Timetable` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `student_id` INTEGER NOT NULL, + `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 + ) + """) + } + + private fun truncateSubjects(database: SupportSQLiteDatabase) { + database.execSQL("DELETE FROM Subjects") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt new file mode 100644 index 00000000..bc0ff900 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt @@ -0,0 +1,15 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration21 : Migration(20, 21) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Attendance ADD COLUMN excusable INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Attendance ADD COLUMN time_id INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Attendance ADD COLUMN excuse_status TEXT DEFAULT NULL") + + database.execSQL("DELETE FROM Semesters") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt new file mode 100644 index 00000000..d67aa2a7 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt @@ -0,0 +1,3 @@ +package io.github.wulkanowy.data.pojos + +class AppCreator(val displayName: String, val githubUsername: String) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt new file mode 100644 index 00000000..095b3716 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.repositories.appcreator + +import android.content.res.AssetManager +import com.google.gson.Gson +import io.github.wulkanowy.data.pojos.AppCreator +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AppCreatorRepository @Inject constructor(private val assets: AssetManager) { + fun getAppCreators(): Single> { + return Single.fromCallable> { + Gson().fromJson( + assets.open("creators.json").bufferedReader().use { it.readText() }, + Array::class.java + ).toList() + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt index cd7702c1..1828c2bd 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt @@ -3,8 +3,11 @@ package io.github.wulkanowy.data.repositories.attendance import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.Absent import io.reactivex.Single import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDateTime +import org.threeten.bp.LocalTime import javax.inject.Inject import javax.inject.Singleton @@ -19,6 +22,7 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) { studentId = semester.studentId, diaryId = semester.diaryId, date = it.date, + timeId = it.timeId, number = it.number, subject = it.subject, name = it.name, @@ -27,9 +31,20 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) { exemption = it.exemption, lateness = it.lateness, excused = it.excused, - deleted = it.deleted + deleted = it.deleted, + excusable = it.excusable, + excuseStatus = it.excuseStatus?.name ) } } } + + fun excuseAbsence(semester: Semester, absenceList: List, reason: String?): Single { + return sdk.switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance -> + Absent( + date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)), + timeId = attendance.timeId + ) + }, reason) + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt index 85102b3c..fc3830c2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt @@ -41,4 +41,8 @@ class AttendanceRepository @Inject constructor( }).map { list -> list.filter { it.date in startDate..endDate } } } } + + fun excuseForAbsence(semester: Semester, attendanceList: List, reason: String? = null): Single { + return remote.excuseAbsence(semester, attendanceList, reason) + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/SentExcuseStatus.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/SentExcuseStatus.kt new file mode 100644 index 00000000..50d6b8ed --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/SentExcuseStatus.kt @@ -0,0 +1,7 @@ +package io.github.wulkanowy.data.repositories.attendance + +enum class SentExcuseStatus(val id: Int = 0) { + WAITING, + ACCEPTED, + DENIED +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt index d636b91a..279e2959 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt @@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.dao.GradeStatisticsDao import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.utils.roundToDecimalPlaces import io.reactivex.Maybe import javax.inject.Inject import javax.inject.Singleton @@ -40,9 +41,9 @@ class GradeStatisticsLocal @Inject constructor( "Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId).flatMap { list -> if (list.isEmpty()) return@flatMap Maybe.empty() Maybe.just(GradePointsStatistics(semester.studentId, semester.semesterId, subjectName, - list.fold(.0) { acc, e -> acc + e.others }, - list.fold(.0) { acc, e -> acc + e.student }) - ) + (list.fold(.0) { acc, e -> acc + e.others } / list.size).roundToDecimalPlaces(2), + (list.fold(.0) { acc, e -> acc + e.student } / list.size).roundToDecimalPlaces(2) + )) } else -> gradePointsStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt index 07a3654a..523caf6c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt @@ -65,6 +65,9 @@ class PreferencesRepository @Inject constructor( val fillMessageContent: Boolean get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content) + val showWholeClassPlan: String + get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class) + private fun getString(id: Int, default: Int) = getString(context.getString(id), default) private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt index 95fddc1b..6377cb78 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt @@ -36,7 +36,7 @@ class SemesterRepository @Inject constructor( local.saveSemesters(new.uniqueSubtract(old)) } } else { - Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}") + Timber.i("Current semesters list:\n${new.joinToString(separator = "\n")}") throw IllegalArgumentException("Current semester can be only one.") } }.flatMap { local.getSemesters(student).toSingle(emptyList()) }) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt index 6036bd40..26e7836a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt @@ -30,6 +30,7 @@ class TimetableRemote @Inject constructor(private val sdk: Sdk) { teacher = it.teacher, teacherOld = it.teacherOld, info = it.info, + studentPlan = it.studentPlan, changes = it.changes, canceled = it.canceled ) 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 22276bdf..d9430a47 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 @@ -16,6 +16,7 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment import io.github.wulkanowy.ui.modules.about.license.LicenseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView @@ -42,6 +43,11 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { Triple(getString(R.string.about_version), "${appInfo.versionName} (${appInfo.versionCode})", getCompatDrawable(R.drawable.ic_all_about)) } + override val creatorsRes: Triple? + get() = context?.run { + Triple(getString(R.string.about_creator), getString(R.string.about_creator_summary), getCompatDrawable(R.drawable.ic_about_creator)) + } + override val feedbackRes: Triple? get() = context?.run { Triple(getString(R.string.about_feedback), getString(R.string.about_feedback_summary), getCompatDrawable(R.drawable.ic_about_feedback)) @@ -143,6 +149,10 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { (activity as? MainActivity)?.pushView(LicenseFragment.newInstance()) } + override fun openCreators() { + (activity as? MainActivity)?.pushView(CreatorFragment.newInstance()) + } + override fun openPrivacyPolicy() { context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt index 8087c955..66293983 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt @@ -52,6 +52,11 @@ class AboutPresenter @Inject constructor( openLicenses() analytics.logEvent("about_open", "name" to "licenses") } + creatorsRes?.first -> { + Timber.i("Opening creators view") + openCreators() + analytics.logEvent("about_open", "name" to "creators") + } privacyRes?.first -> { Timber.i("Opening privacy page ") openPrivacyPolicy() @@ -65,6 +70,7 @@ class AboutPresenter @Inject constructor( view?.run { updateData(AboutScrollableHeader(), listOfNotNull( versionRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, + creatorsRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, feedbackRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, faqRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, discordRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt index 34850bae..228225ec 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt @@ -7,6 +7,8 @@ interface AboutView : BaseView { val versionRes: Triple? + val creatorsRes: Triple? + val feedbackRes: Triple? val faqRes: Triple? @@ -33,5 +35,7 @@ interface AboutView : BaseView { fun openLicenses() + fun openCreators() + fun openPrivacyPolicy() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorFragment.kt new file mode 100644 index 00000000..299657ba --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorFragment.kt @@ -0,0 +1,76 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import eu.davidea.flexibleadapter.FlexibleAdapter +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.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_creator.* +import javax.inject.Inject + +class CreatorFragment : BaseFragment(), CreatorView, MainView.TitledView { + + @Inject + lateinit var presenter: CreatorPresenter + + @Inject + lateinit var creatorsAdapter: FlexibleAdapter> + + override val titleStringId get() = R.string.creators_title + + companion object { + fun newInstance() = CreatorFragment() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_creator, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + with(creatorRecycler) { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = creatorsAdapter + addItemDecoration(FlexibleItemDecoration(context) + .withDefaultDivider() + .withDrawDividerOnLastItem(false)) + } + creatorsAdapter.setOnItemClickListener(presenter::onItemSelected) + creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() } + } + + override fun updateData(data: List) { + creatorsAdapter.updateDataSet(data) + } + + override fun openUserGithubPage(username: String) { + context?.openInternetBrowser("https://github.com/${username}", ::showMessage) + } + + override fun openGithubContributorsPage() { + context?.openInternetBrowser("https://github.com/wulkanowy/wulkanowy/graphs/contributors", ::showMessage) + } + + override fun showProgress(show: Boolean) { + creatorProgress.visibility = if (show) VISIBLE else GONE + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorItem.kt new file mode 100644 index 00000000..909919bb --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorItem.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import android.view.View +import coil.api.load +import coil.transform.RoundedCornersTransformation +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.pojos.AppCreator +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_creator.* + +class CreatorItem(val creator: AppCreator) : AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_creator + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>) = ViewHolder(view, adapter) + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) { + with(holder) { + creatorItemName.text = creator.displayName + + creatorItemAvatar.load("https://github.com/${creator.githubUsername}.png") { + transformations(RoundedCornersTransformation(8f)) + crossfade(true) + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as CreatorItem + + if (creator != other.creator) return false + + return true + } + + override fun hashCode() = creator.hashCode() + + class ViewHolder(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/about/creator/CreatorPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorPresenter.kt new file mode 100644 index 00000000..bc8034ab --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorPresenter.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.pojos.AppCreator +import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository +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.SchedulersProvider +import io.reactivex.Single +import javax.inject.Inject + +class CreatorPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val appCreatorRepository: AppCreatorRepository +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: CreatorView) { + super.onAttachView(view) + view.initView() + loadData() + } + + fun onItemSelected(item: AbstractFlexibleItem<*>) { + if (item !is CreatorItem) return + view?.openUserGithubPage(item.creator.githubUsername) + } + + fun onSeeMoreClick() { + view?.openGithubContributorsPage() + } + + private fun loadData() { + disposable.add(appCreatorRepository.getAppCreators() + .map { it.map { creator -> CreatorItem(creator) } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { view?.showProgress(false) } + .subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) })) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorView.kt new file mode 100644 index 00000000..2a6a4303 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorView.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import io.github.wulkanowy.ui.base.BaseView + +interface CreatorView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun openUserGithubPage(username: String) + + fun openGithubContributorsPage() + + fun showProgress(show: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt new file mode 100644 index 00000000..75f99840 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.ui.modules.attendance + +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.IFlexible +import io.github.wulkanowy.data.db.entities.Attendance + +class AttendanceAdapter> : FlexibleAdapter(null, null, true) { + + var excuseActionMode: Boolean = false + + var onExcuseCheckboxSelect: (attendanceItem: Attendance, checked: Boolean) -> Unit = { _, _ -> } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt index bc7c1cac..9969b1c7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.attendance +import android.content.DialogInterface.BUTTON_POSITIVE import android.os.Bundle import android.view.LayoutInflater import android.view.Menu @@ -10,8 +11,9 @@ import android.view.View.GONE import android.view.View.INVISIBLE import android.view.View.VISIBLE import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.view.ActionMode import com.wdullaer.materialdatetimepicker.date.DatePickerDialog -import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.FlexibleItemDecoration import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -24,6 +26,7 @@ import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.SchooldaysRangeLimiter import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.dialog_excuse.* import kotlinx.android.synthetic.main.fragment_attendance.* import org.threeten.bp.LocalDate import javax.inject.Inject @@ -35,7 +38,13 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie lateinit var presenter: AttendancePresenter @Inject - lateinit var attendanceAdapter: FlexibleAdapter> + lateinit var attendanceAdapter: AttendanceAdapter> + + override val excuseSuccessString: String + get() = getString(R.string.attendance_excuse_success) + + override val excuseNoSelectionString: String + get() = getString(R.string.attendance_excuse_no_selection) companion object { private const val SAVED_DATE_KEY = "CURRENT_DATE" @@ -49,6 +58,34 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize + override val excuseActionMode: Boolean get() = attendanceAdapter.excuseActionMode + + private var actionMode: ActionMode? = null + private val actionModeCallback = object : ActionMode.Callback { + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + val inflater = mode.menuInflater + inflater.inflate(R.menu.context_menu_excuse, menu) + return true + } + + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { + mode.title = getString(R.string.attendance_excuse_title) + return presenter.onPrepareActionMode() + } + + override fun onDestroyActionMode(mode: ActionMode) { + presenter.onDestroyActionMode() + actionMode = null + } + + override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean { + return when (menu.itemId) { + R.id.excuseMenuSubmit -> presenter.onExcuseSubmitButtonClick() + else -> false + } + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -66,6 +103,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie override fun initView() { attendanceAdapter.setOnItemClickListener(presenter::onAttendanceItemSelected) + attendanceAdapter.onExcuseCheckboxSelect = presenter::onExcuseCheckboxSelect with(attendanceRecycler) { layoutManager = SmoothScrollLinearLayoutManager(context) @@ -83,6 +121,8 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie attendanceNavDate.setOnClickListener { presenter.onPickDate() } attendanceNextButton.setOnClickListener { presenter.onNextDay() } + attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() } + attendanceNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } @@ -115,6 +155,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie if (::presenter.isInitialized) presenter.onViewReselected() } + override fun onFragmentChanged() { + if (::presenter.isInitialized) presenter.onMainViewChanged() + } + override fun popView() { (activity as? MainActivity)?.popView() } @@ -155,6 +199,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie attendanceNextButton.visibility = if (show) VISIBLE else INVISIBLE } + override fun showExcuseButton(show: Boolean) { + attendanceExcuseButton.visibility = if (show) VISIBLE else GONE + } + override fun showAttendanceDialog(lesson: Attendance) { (activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson)) } @@ -174,10 +222,38 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie } } + override fun showExcuseDialog() { + AlertDialog.Builder(requireContext()) + .setTitle(R.string.attendance_excuse_title) + .setView(R.layout.dialog_excuse) + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .create() + .apply { + setButton(BUTTON_POSITIVE, getString(R.string.attendance_excuse_dialog_submit)) { _, _ -> + presenter.onExcuseDialogSubmit(excuseReason.text?.toString().orEmpty()) + } + }.show() + } + override fun openSummaryView() { (activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance()) } + override fun startActionMode() { + actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback) + } + + override fun showExcuseCheckboxes(show: Boolean) { + attendanceAdapter.apply { + excuseActionMode = show + notifyDataSetChanged() + } + } + + override fun finishActionMode() { + actionMode?.finish() + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt index 16a140cb..7355aec2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt @@ -1,18 +1,22 @@ package io.github.wulkanowy.ui.modules.attendance import android.view.View +import android.view.View.GONE import android.view.View.INVISIBLE import android.view.View.VISIBLE +import androidx.core.view.isVisible 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.Attendance +import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.item_attendance.* -class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem() { +class AttendanceItem(val attendance: Attendance) : + AbstractFlexibleItem() { override fun getLayoutRes() = R.layout.item_attendance @@ -26,6 +30,34 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem + (adapter as AttendanceAdapter).onExcuseCheckboxSelect(attendance, checked) + } + + when (if (attendance.excuseStatus != null) SentExcuseStatus.valueOf(attendance.excuseStatus) else null) { + SentExcuseStatus.WAITING -> { + attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_waiting) + attendanceItemExcuseInfo.visibility = VISIBLE + attendanceItemAlert.visibility = INVISIBLE + } + SentExcuseStatus.DENIED -> { + attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_denied) + attendanceItemExcuseInfo.visibility = VISIBLE + } + else -> { + if (attendance.excusable && (adapter as AttendanceAdapter).excuseActionMode) { + attendanceItemNumber.visibility = GONE + attendanceItemExcuseCheckbox.visibility = VISIBLE + } else { + attendanceItemNumber.visibility = VISIBLE + attendanceItemExcuseCheckbox.visibility = GONE + } + } + } } } @@ -46,8 +78,20 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem) : FlexibleViewHolder(view, adapter), LayoutContainer { + class ViewHolder(view: View, val adapter: FlexibleAdapter<*>) : + FlexibleViewHolder(view, adapter), + LayoutContainer { + override val containerView: View get() = contentView + + override fun onClick(view: View?) { + super.onClick(view) + attendanceItemExcuseCheckbox.apply { + if ((adapter as AttendanceAdapter).excuseActionMode && isVisible) { + isChecked = !isChecked + } + } + } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceModule.kt new file mode 100644 index 00000000..eb35fea1 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceModule.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.ui.modules.attendance + +import dagger.Module +import dagger.Provides +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem + +@Module +class AttendanceModule { + + @Provides + fun provideAttendanceFlexibleAdapter() = AttendanceAdapter>() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index 1490ee6e..679d5367 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.attendance import android.annotation.SuppressLint import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository @@ -40,6 +41,8 @@ class AttendancePresenter @Inject constructor( private lateinit var lastError: Throwable + private val attendanceToExcuseList = mutableListOf() + fun onAttachView(view: AttendanceView, date: Long?) { super.onAttachView(view) view.initView() @@ -51,11 +54,15 @@ class AttendancePresenter @Inject constructor( } fun onPreviousDay() { + view?.finishActionMode() + attendanceToExcuseList.clear() loadData(currentDate.previousSchoolDay) reloadView() } fun onNextDay() { + view?.finishActionMode() + attendanceToExcuseList.clear() loadData(currentDate.nextSchoolDay) reloadView() } @@ -100,10 +107,59 @@ class AttendancePresenter @Inject constructor( } } + fun onMainViewChanged() { + view?.finishActionMode() + } + fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is AttendanceItem) { - Timber.i("Select attendance item ${item.attendance.id}") - view?.showAttendanceDialog(item.attendance) + view?.apply { + if (item is AttendanceItem && !excuseActionMode) { + Timber.i("Select attendance item ${item.attendance.id}") + showAttendanceDialog(item.attendance) + } + } + } + + fun onExcuseButtonClick() { + view?.startActionMode() + } + + fun onExcuseCheckboxSelect(attendanceItem: Attendance, checked: Boolean) { + if (checked) attendanceToExcuseList.add(attendanceItem) + else attendanceToExcuseList.remove(attendanceItem) + } + + fun onExcuseSubmitButtonClick(): Boolean { + view?.apply { + return if (attendanceToExcuseList.isNotEmpty()) { + showExcuseDialog() + true + } else { + showMessage(excuseNoSelectionString) + false + } + } + return false + } + + fun onExcuseDialogSubmit(reason: String) { + view?.finishActionMode() + excuseAbsence(if (reason != "") reason else null, attendanceToExcuseList.toList()) + } + + fun onPrepareActionMode(): Boolean { + view?.apply { + showExcuseCheckboxes(true) + showExcuseButton(false) + } + attendanceToExcuseList.clear() + return true + } + + fun onDestroyActionMode() { + view?.apply { + showExcuseCheckboxes(false) + showExcuseButton(true) } } @@ -157,6 +213,7 @@ class AttendancePresenter @Inject constructor( showEmpty(it.isEmpty()) showErrorView(false) showContent(it.isNotEmpty()) + showExcuseButton(it.any { item -> item.attendance.excusable }) } analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh) }) { @@ -167,6 +224,39 @@ class AttendancePresenter @Inject constructor( } } + private fun excuseAbsence(reason: String?, toExcuseList: List) { + Timber.i("Excusing absence started") + disposable.apply { + add(studentRepository.getCurrentStudent() + .delay(200, MILLISECONDS) + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { attendanceRepository.excuseForAbsence(it, toExcuseList, reason) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.apply { + showProgress(true) + showContent(false) + showExcuseButton(false) + } + } + .subscribe({ + Timber.i("Excusing for absence result: Success") + analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size) + attendanceToExcuseList.clear() + view?.apply { + showExcuseButton(false) + showMessage(excuseSuccessString) + } + loadData(currentDate, true) + }) { + Timber.i("Excusing for absence result: An exception occurred") + view?.showProgress(false) + errorHandler.dispatch(it) + }) + } + } + private fun showErrorViewOnError(message: String, error: Throwable) { view?.run { if (isViewEmpty) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt index a6d0d4ba..03e95053 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt @@ -10,6 +10,12 @@ interface AttendanceView : BaseView { val currentStackSize: Int? + val excuseSuccessString: String + + val excuseNoSelectionString: String + + val excuseActionMode: Boolean + fun initView() fun updateData(data: List) @@ -38,11 +44,21 @@ interface AttendanceView : BaseView { fun showNextButton(show: Boolean) + fun showExcuseButton(show: Boolean) + fun showAttendanceDialog(lesson: Attendance) fun showDatePickerDialog(currentDate: LocalDate) + fun showExcuseDialog() + fun openSummaryView() + fun startActionMode() + + fun showExcuseCheckboxes(show: Boolean) + + fun finishActionMode() + fun popView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt index 88635c37..37bba4d4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt @@ -18,7 +18,11 @@ class GradeAverageProvider @Inject constructor( private val gradeSummaryRepository: GradeSummaryRepository ) { - fun getGradeAverage(student: Student, semesters: List, selectedSemesterId: Int, forceRefresh: Boolean): Single> { + private val plusModifier = preferencesRepository.gradePlusModifier + + private val minusModifier = preferencesRepository.gradeMinusModifier + + fun getGradeAverage(student: Student, semesters: List, selectedSemesterId: Int, forceRefresh: Boolean): Single>> { return when (preferencesRepository.gradeAverageMode) { "all_year" -> getAllYearAverage(student, semesters, selectedSemesterId, forceRefresh) "only_one_semester" -> getOnlyOneSemesterAverage(student, semesters, selectedSemesterId, forceRefresh) @@ -26,11 +30,9 @@ class GradeAverageProvider @Inject constructor( } } - private fun getAllYearAverage(student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean): Single> { + private fun getAllYearAverage(student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean): Single>> { val selectedSemester = semesters.single { it.semesterId == semesterId } val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 } - val plusModifier = preferencesRepository.gradePlusModifier - val minusModifier = preferencesRepository.gradeMinusModifier return getAverageFromGradeSummary(selectedSemester, forceRefresh) .switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh) @@ -43,30 +45,28 @@ class GradeAverageProvider @Inject constructor( }.map { grades -> grades.map { if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) else it } .groupBy { it.subject } - .mapValues { it.value.calcAverage() } + .map { Triple(it.key, it.value.calcAverage(), "") } }) } - private fun getOnlyOneSemesterAverage(student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean): Single> { + private fun getOnlyOneSemesterAverage(student: Student, semesters: List, semesterId: Int, forceRefresh: Boolean): Single>> { val selectedSemester = semesters.single { it.semesterId == semesterId } - val plusModifier = preferencesRepository.gradePlusModifier - val minusModifier = preferencesRepository.gradeMinusModifier return getAverageFromGradeSummary(selectedSemester, forceRefresh) .switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh) .map { grades -> grades.map { if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) else it } .groupBy { it.subject } - .mapValues { it.value.calcAverage() } + .map { Triple(it.key, it.value.calcAverage(), "") } }) } - private fun getAverageFromGradeSummary(selectedSemester: Semester, forceRefresh: Boolean): Maybe> { + private fun getAverageFromGradeSummary(selectedSemester: Semester, forceRefresh: Boolean): Maybe>> { return gradeSummaryRepository.getGradesSummary(selectedSemester, forceRefresh) .toMaybe() .flatMap { if (it.any { summary -> summary.average != .0 }) { - Maybe.just(it.map { summary -> summary.subject to summary.average }.toMap()) + Maybe.just(it.map { summary -> Triple(summary.subject, summary.average, summary.pointsSum) }) } else Maybe.empty() }.filter { !preferencesRepository.gradeAverageForceCalc } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt index 0abeaeea..a8f8a865 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt @@ -10,6 +10,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.utils.colorStringId import io.github.wulkanowy.utils.getBackgroundColor +import io.github.wulkanowy.utils.getGradeColor import io.github.wulkanowy.utils.toFormattedString import kotlinx.android.synthetic.main.dialog_grade.* @@ -50,7 +51,12 @@ class GradeDetailsDialog : DialogFragment() { super.onActivityCreated(savedInstanceState) gradeDialogSubject.text = grade.subject - gradeDialogWeightValue.text = grade.weight + + gradeDialogColorAndWeightValue.run { + text = context.getString(R.string.grade_weight_value, grade.weight) + setBackgroundResource(grade.getGradeColor()) + } + gradeDialogDateValue.text = grade.date.toFormattedString() gradeDialogColorValue.text = getString(grade.colorStringId) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt index 195ae11d..9505d354 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt @@ -45,6 +45,9 @@ class GradeDetailsFragment : BaseFragment(), GradeDetailsView, GradeView.GradeCh override val averageString: String get() = getString(R.string.grade_average) + override val pointsSumString: String + get() = getString(R.string.grade_points_sum) + override val weightString: String get() = getString(R.string.grade_weight) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt index 9ed41280..4a34a145 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt @@ -15,6 +15,7 @@ class GradeDetailsHeader( private val subject: String, private val number: String, private val average: String, + private val pointsSum: String, var newGrades: Int, private val isExpandable: Boolean ) : AbstractExpandableItem() { @@ -36,6 +37,8 @@ class GradeDetailsHeader( maxLines = if (isExpanded) 2 else 1 } gradeHeaderAverage.text = average + gradeHeaderPointsSum.text = pointsSum + gradeHeaderPointsSum.visibility = if (pointsSum.isNotEmpty()) VISIBLE else GONE gradeHeaderNumber.text = number gradeHeaderNote.visibility = if (newGrades > 0) VISIBLE else GONE if (newGrades > 0) gradeHeaderNote.text = newGrades.toString(10) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index 647047c6..a9e5b2b7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -180,22 +180,24 @@ class GradeDetailsPresenter @Inject constructor( } } - private fun createGradeItems(items: Map>, averages: Map): List { + private fun createGradeItems(items: Map>, averages: List>): List { val isGradeExpandable = preferencesRepository.isGradeExpandable val gradeColorTheme = preferencesRepository.gradeColorTheme val noDescriptionString = view?.noDescriptionString.orEmpty() val weightString = view?.weightString.orEmpty() + val pointsSumString = view?.pointsSumString.orEmpty() - return items.map { + return items.map { subject -> GradeDetailsHeader( - subject = it.key, - average = formatAverage(averages[it.key]), - number = view?.getGradeNumberString(it.value.size).orEmpty(), - newGrades = it.value.filter { grade -> !grade.isRead }.size, + subject = subject.key, + average = formatAverage(averages.singleOrNull { subject.key == it.first }?.second), + pointsSum = averages.singleOrNull { subject.key == it.first }?.takeIf { it.third.isNotEmpty() }?.let { pointsSumString.format(it.third) }.orEmpty(), + number = view?.getGradeNumberString(subject.value.size).orEmpty(), + newGrades = subject.value.filter { grade -> !grade.isRead }.size, isExpandable = isGradeExpandable ).apply { - subItems = it.value.map { item -> + subItems = subject.value.map { item -> GradeDetailsItem( grade = item, valueBgColor = item.getBackgroundColor(gradeColorTheme), diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt index dad4ac6e..e2977bcb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsView.kt @@ -14,6 +14,8 @@ interface GradeDetailsView : BaseView { val averageString: String + val pointsSumString: String + val weightString: String val noDescriptionString: String diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt index a65cab9c..1c54f387 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt @@ -108,6 +108,17 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G animateXY(1000, 1000) legend.textColor = context.getThemeAttrColor(android.R.attr.textColorPrimary) + + with(axisLeft) { + axisMinimum = 0f + axisMaximum = 100f + labelCount = 11 + } + with(axisRight) { + axisMinimum = 0f + axisMaximum = 100f + labelCount = 11 + } } subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf()) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt index bea70ea4..83378261 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt @@ -95,7 +95,7 @@ class GradeStatisticsPresenter @Inject constructor( } fun onTypeChange() { - val type = view?.let { it.currentType } ?: ViewType.POINTS + val type = view?.currentType ?: ViewType.POINTS Timber.i("Select grade stats semester: $type") disposable.clear() view?.run { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt index 1aaa9d09..5f441277 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -115,27 +115,26 @@ class GradeSummaryPresenter @Inject constructor( disposable.clear() } - private fun createGradeSummaryItemsAndHeader(gradesSummary: List, averages: Map) - : Pair, GradeSummaryScrollableHeader> { - return averages.filterValues { value -> value != 0.0 } + private fun createGradeSummaryItemsAndHeader(gradesSummary: List, averages: List>): Pair, GradeSummaryScrollableHeader> { + return averages.filter { value -> value.second != 0.0 } .let { filteredAverages -> gradesSummary.filter { !checkEmpty(it, filteredAverages) } - .map { + .map { gradeSummary -> GradeSummaryItem( - summary = it, - average = formatAverage(filteredAverages.getOrElse(it.subject) { 0.0 }, "") + summary = gradeSummary, + average = formatAverage(filteredAverages.singleOrNull { gradeSummary.subject == it.first }?.second ?: .0, "") ) }.let { it to GradeSummaryScrollableHeader( formatAverage(gradesSummary.calcAverage()), - formatAverage(filteredAverages.values.average())) + formatAverage(filteredAverages.map { values -> values.second }.average())) } } } - private fun checkEmpty(gradeSummary: GradeSummary, averages: Map): Boolean { + private fun checkEmpty(gradeSummary: GradeSummary, averages: List>): Boolean { return gradeSummary.run { - finalGrade.isBlank() && predictedGrade.isBlank() && averages[subject] == null + finalGrade.isBlank() && predictedGrade.isBlank() && averages.singleOrNull { it.first == subject } == null } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index 991c1d10..ae4be953 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -36,6 +36,7 @@ import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.safelyPopFragments import io.github.wulkanowy.utils.setOnViewChangeListener import kotlinx.android.synthetic.main.activity_main.* +import timber.log.Timber import javax.inject.Inject class MainActivity : BaseActivity(), MainView { @@ -167,6 +168,11 @@ class MainActivity : BaseActivity(), MainView { (navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected() } + override fun notifyMenuViewChanged() { + Timber.d("Menu view changed") + (navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentChanged() + } + fun showDialogFragment(dialog: DialogFragment) { navController.showDialogFragment(dialog) } 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 bbaea51c..50e5896f 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 @@ -8,10 +8,12 @@ import dagger.android.ContributesAndroidInjector import io.github.wulkanowy.R import io.github.wulkanowy.di.scopes.PerFragment import io.github.wulkanowy.ui.modules.about.AboutFragment +import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment import io.github.wulkanowy.ui.modules.about.license.LicenseFragment import io.github.wulkanowy.ui.modules.about.license.LicenseModule import io.github.wulkanowy.ui.modules.account.AccountDialog import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment +import io.github.wulkanowy.ui.modules.attendance.AttendanceModule import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment import io.github.wulkanowy.ui.modules.exam.ExamFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment @@ -50,7 +52,7 @@ abstract class MainModule { } @PerFragment - @ContributesAndroidInjector + @ContributesAndroidInjector(modules = [AttendanceModule::class]) abstract fun bindAttendanceFragment(): AttendanceFragment @PerFragment @@ -121,6 +123,10 @@ abstract class MainModule { @ContributesAndroidInjector(modules = [LicenseModule::class]) abstract fun bindLicenseFragment(): LicenseFragment + @PerFragment + @ContributesAndroidInjector() + abstract fun bindCreatorsFragment(): CreatorFragment + @PerFragment @ContributesAndroidInjector(modules = [SchoolAndTeachersModule::class]) abstract fun bindSchoolAndTeachersFragment(): SchoolAndTeachersFragment 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 1c0fd39f..8eccbc67 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 @@ -75,6 +75,7 @@ class MainPresenter @Inject constructor( notifyMenuViewReselected() false } else { + notifyMenuViewChanged() switchMenuView(index) true } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt index 033b6a1f..0d8b1416 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt @@ -26,6 +26,8 @@ interface MainView : BaseView { fun notifyMenuViewReselected() + fun notifyMenuViewChanged() + fun setViewTitle(title: String) fun popView(depth: Int = 1) @@ -33,6 +35,8 @@ interface MainView : BaseView { interface MainChildView { fun onFragmentReselected() + + fun onFragmentChanged() {} } interface TitledView { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt index 0991981d..99aca2a2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt @@ -15,11 +15,15 @@ import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.toFormattedString import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.item_timetable.* +import kotlinx.android.synthetic.main.item_timetable_small.* -class TimetableItem(val lesson: Timetable) : +class TimetableItem(val lesson: Timetable, private val showWholeClassPlan: String) : AbstractFlexibleItem() { - override fun getLayoutRes() = R.layout.item_timetable + override fun getLayoutRes() = when { + showWholeClassPlan == "small" && !lesson.studentPlan -> R.layout.item_timetable_small + else -> R.layout.item_timetable + } override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { return ViewHolder(view, adapter) @@ -27,16 +31,29 @@ class TimetableItem(val lesson: Timetable) : @SuppressLint("SetTextI18n") override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - updateFields(holder) + when (itemViewType) { + R.layout.item_timetable_small -> { + with(holder) { + timetableSmallItemNumber.text = lesson.number.toString() + timetableSmallItemSubject.text = lesson.subject + timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm") + timetableSmallItemRoom.text = lesson.room + timetableSmallItemTeacher.text = lesson.teacher + } + } + R.layout.item_timetable -> { + updateFields(holder) - with(holder) { - timetableItemSubject.paintFlags = - if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG - else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() + with(holder) { + timetableItemSubject.paintFlags = + if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG + else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() + } + + updateDescription(holder) + updateColors(holder) + } } - - updateDescription(holder) - updateColors(holder) } private fun updateFields(holder: ViewHolder) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt index 4c30ecb7..d9eb4478 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -2,6 +2,8 @@ package io.github.wulkanowy.ui.modules.timetable import android.annotation.SuppressLint import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository @@ -29,6 +31,7 @@ class TimetablePresenter @Inject constructor( studentRepository: StudentRepository, private val timetableRepository: TimetableRepository, private val semesterRepository: SemesterRepository, + private val prefRepository: PreferencesRepository, private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler, studentRepository, schedulers) { @@ -134,7 +137,7 @@ class TimetablePresenter @Inject constructor( .flatMap { semesterRepository.getCurrentSemester(it) } .delay(200, MILLISECONDS) .flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) } - .map { items -> items.map { TimetableItem(it) } } + .map { createTimetableItems(it) } .map { items -> items.sortedBy { it.lesson.number } } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) @@ -172,6 +175,12 @@ class TimetablePresenter @Inject constructor( } } + private fun createTimetableItems(items: List): List { + return items + .filter { if (prefRepository.showWholeClassPlan == "no") it.studentPlan else true } + .map { TimetableItem(it, prefRepository.showWholeClassPlan) } + } + private fun reloadView() { Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}") view?.apply { diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt index c1e182ba..c57b6247 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt @@ -24,14 +24,7 @@ fun List.calcAverage(): Double { fun Grade.getBackgroundColor(theme: String): Int { return when (theme) { - "grade_color" -> when (color) { - "000000" -> R.color.grade_black - "F04C4C" -> R.color.grade_red - "20A4F7" -> R.color.grade_blue - "6ECD07" -> R.color.grade_green - "B16CF1" -> R.color.grade_purple - else -> R.color.grade_material_default - } + "grade_color" -> getGradeColor() "material" -> when (value.toInt()) { 6 -> R.color.grade_material_six 5 -> R.color.grade_material_five @@ -53,6 +46,17 @@ fun Grade.getBackgroundColor(theme: String): Int { } } +fun Grade.getGradeColor(): Int { + return when (color) { + "000000" -> R.color.grade_black + "F04C4C" -> R.color.grade_red + "20A4F7" -> R.color.grade_blue + "6ECD07" -> R.color.grade_green + "B16CF1" -> R.color.grade_purple + else -> R.color.grade_material_default + } +} + inline val Grade.colorStringId: Int get() { return when (color) { diff --git a/app/src/main/java/io/github/wulkanowy/utils/NumberExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/NumberExtension.kt new file mode 100644 index 00000000..12350a42 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/NumberExtension.kt @@ -0,0 +1,7 @@ +package io.github.wulkanowy.utils + +import kotlin.math.round + +fun Double.roundToDecimalPlaces(places: Int = 2): Double { + return round(this * 10 * places) / (10 * places) +} 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 467c3ec8..813eeace 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,6 +1,8 @@ -Wersja 0.14.2 +Wersja 0.15.0 -- naprawiliśmy błąd powodujący zatrzymanie aplikacji na urządzeniach z Androidem poniżej 5.0 -- zmieniliśmy mało mówiący komunikat o niedostępnym dzienniku na taki mówiący o przerwie technicznej dziennika +- dodaliśmy funkcję usprawiedliwiania nieobecności +- w trybie hybrydowym i mobilnym pokazują się teraz domyślnie tylko lekcje grupy ucznia +- odświezyliśmy wygląd okienka ze szczegółami oceny +- dokonaliśmy kilku poprawek dla systemu punktowego: suma punktów wyświetla się teraz w szczegółach ocen, a w widoku ucznia na tle klasy wykres jest w końcu poprawnie skalowany Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases diff --git a/app/src/main/res/drawable-anydpi-v24/ic_stat_grade.xml b/app/src/main/res/drawable-anydpi-v24/ic_stat_grade.xml index 2774dd5d..21b406fd 100644 --- a/app/src/main/res/drawable-anydpi-v24/ic_stat_grade.xml +++ b/app/src/main/res/drawable-anydpi-v24/ic_stat_grade.xml @@ -3,11 +3,7 @@ android:height="24dp" android:viewportWidth="28.26087" android:viewportHeight="28.26087"> - - - + diff --git a/app/src/main/res/drawable-anydpi-v24/ic_stat_luckynumber.xml b/app/src/main/res/drawable-anydpi-v24/ic_stat_luckynumber.xml index 4699c457..bee1c707 100644 --- a/app/src/main/res/drawable-anydpi-v24/ic_stat_luckynumber.xml +++ b/app/src/main/res/drawable-anydpi-v24/ic_stat_luckynumber.xml @@ -3,11 +3,7 @@ android:height="24dp" android:viewportWidth="26.086956" android:viewportHeight="26.086956"> - - - + diff --git a/app/src/main/res/drawable-anydpi-v24/ic_stat_message.xml b/app/src/main/res/drawable-anydpi-v24/ic_stat_message.xml index cb42d7cc..89a8aef2 100644 --- a/app/src/main/res/drawable-anydpi-v24/ic_stat_message.xml +++ b/app/src/main/res/drawable-anydpi-v24/ic_stat_message.xml @@ -3,11 +3,7 @@ android:height="24dp" android:viewportWidth="26.086956" android:viewportHeight="26.086956"> - - - + diff --git a/app/src/main/res/drawable-anydpi-v24/ic_stat_note.xml b/app/src/main/res/drawable-anydpi-v24/ic_stat_note.xml index d6b0d4d1..0f335336 100644 --- a/app/src/main/res/drawable-anydpi-v24/ic_stat_note.xml +++ b/app/src/main/res/drawable-anydpi-v24/ic_stat_note.xml @@ -3,11 +3,7 @@ android:height="24dp" android:viewportWidth="26.086956" android:viewportHeight="26.086956"> - - - + diff --git a/app/src/main/res/drawable-night/background_header_note.xml b/app/src/main/res/drawable-night/background_header_note.xml index 2983b32c..6b594e7c 100644 --- a/app/src/main/res/drawable-night/background_header_note.xml +++ b/app/src/main/res/drawable-night/background_header_note.xml @@ -1,5 +1,5 @@ - - + + diff --git a/app/src/main/res/drawable/background_header_note.xml b/app/src/main/res/drawable/background_header_note.xml index 8e92e3ab..c21e55c6 100644 --- a/app/src/main/res/drawable/background_header_note.xml +++ b/app/src/main/res/drawable/background_header_note.xml @@ -1,5 +1,5 @@ - - - \ No newline at end of file + + + diff --git a/app/src/main/res/drawable/ic_about_creator.xml b/app/src/main/res/drawable/ic_about_creator.xml new file mode 100644 index 00000000..c3daf609 --- /dev/null +++ b/app/src/main/res/drawable/ic_about_creator.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_done.xml b/app/src/main/res/drawable/ic_all_done.xml new file mode 100644 index 00000000..bb657f6e --- /dev/null +++ b/app/src/main/res/drawable/ic_all_done.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_done_all.xml b/app/src/main/res/drawable/ic_all_done_all.xml new file mode 100644 index 00000000..e27672ef --- /dev/null +++ b/app/src/main/res/drawable/ic_all_done_all.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_excuse_denied.xml b/app/src/main/res/drawable/ic_excuse_denied.xml new file mode 100644 index 00000000..218cfbdc --- /dev/null +++ b/app/src/main/res/drawable/ic_excuse_denied.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_excuse_waiting.xml b/app/src/main/res/drawable/ic_excuse_waiting.xml new file mode 100644 index 00000000..863418b7 --- /dev/null +++ b/app/src/main/res/drawable/ic_excuse_waiting.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_main_grade.xml b/app/src/main/res/drawable/ic_main_grade.xml index 7577f399..bf4c2cf2 100644 --- a/app/src/main/res/drawable/ic_main_grade.xml +++ b/app/src/main/res/drawable/ic_main_grade.xml @@ -1,7 +1,9 @@ - + diff --git a/app/src/main/res/drawable/ic_main_more.xml b/app/src/main/res/drawable/ic_main_more.xml index 1b436a32..f57a7245 100644 --- a/app/src/main/res/drawable/ic_main_more.xml +++ b/app/src/main/res/drawable/ic_main_more.xml @@ -1,7 +1,9 @@ - + diff --git a/app/src/main/res/layout/dialog_excuse.xml b/app/src/main/res/layout/dialog_excuse.xml new file mode 100644 index 00000000..4ed9294c --- /dev/null +++ b/app/src/main/res/layout/dialog_excuse.xml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_grade.xml b/app/src/main/res/layout/dialog_grade.xml index f95b9cfd..be0570c2 100644 --- a/app/src/main/res/layout/dialog_grade.xml +++ b/app/src/main/res/layout/dialog_grade.xml @@ -17,90 +17,84 @@ android:padding="20dp" tools:ignore="UselessParent"> - + android:orientation="vertical"> - + + + + + + android:minHeight="120dp" + android:orientation="vertical"> - + - - - - - + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_login_advanced.xml b/app/src/main/res/layout/fragment_login_advanced.xml index 15e0904b..2174716a 100644 --- a/app/src/main/res/layout/fragment_login_advanced.xml +++ b/app/src/main/res/layout/fragment_login_advanced.xml @@ -101,7 +101,7 @@ android:id="@+id/loginFormName" android:layout_width="match_parent" android:layout_height="wrap_content" - android:autofillHints="emailAddress" + android:autofillHints="username|emailAddress" android:inputType="textEmailAddress" android:maxLines="1" tools:targetApi="o" /> @@ -121,6 +121,7 @@ android:layout_marginRight="24dp" android:hint="@string/login_password_hint" app:errorEnabled="true" + app:errorIconDrawable="@null" app:layout_constraintBottom_toTopOf="@+id/loginFormHostLayout" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -216,6 +217,7 @@ android:layout_height="wrap_content" android:imeActionLabel="@string/login_sign_in" android:imeOptions="actionDone" + android:importantForAutofill="no" android:inputType="textAutoComplete|textNoSuggestions" android:maxLines="1" tools:ignore="LabelFor" /> diff --git a/app/src/main/res/layout/fragment_login_form.xml b/app/src/main/res/layout/fragment_login_form.xml index bb93c182..13880ff7 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -133,7 +133,7 @@ android:id="@+id/loginFormName" android:layout_width="match_parent" android:layout_height="wrap_content" - android:autofillHints="emailAddress" + android:autofillHints="username|emailAddress" android:inputType="textEmailAddress" android:maxLines="1" tools:targetApi="o" /> @@ -153,6 +153,7 @@ android:layout_marginRight="24dp" android:hint="@string/login_password_hint" app:errorEnabled="true" + app:errorIconDrawable="@null" app:layout_constraintBottom_toTopOf="@+id/loginFormHostLayout" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/header_grade_details.xml b/app/src/main/res/layout/header_grade_details.xml index 0fa3872e..75e01745 100644 --- a/app/src/main/res/layout/header_grade_details.xml +++ b/app/src/main/res/layout/header_grade_details.xml @@ -1,17 +1,16 @@ + tools:context=".ui.modules.grade.details.GradeDetailsHeader"> + + diff --git a/app/src/main/res/layout/item_attendance.xml b/app/src/main/res/layout/item_attendance.xml index 95a8f017..56bcab44 100644 --- a/app/src/main/res/layout/item_attendance.xml +++ b/app/src/main/res/layout/item_attendance.xml @@ -13,16 +13,40 @@ android:paddingBottom="7dp" tools:context=".ui.modules.attendance.AttendanceItem"> - + android:orientation="vertical"> + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_timetable_small.xml b/app/src/main/res/layout/item_timetable_small.xml new file mode 100644 index 00000000..39e84ae0 --- /dev/null +++ b/app/src/main/res/layout/item_timetable_small.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/context_menu_excuse.xml b/app/src/main/res/menu/context_menu_excuse.xml new file mode 100644 index 00000000..4eff14e3 --- /dev/null +++ b/app/src/main/res/menu/context_menu_excuse.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/app/src/main/res/values-pl/preferences_values.xml b/app/src/main/res/values-pl/preferences_values.xml index 33a66e0f..c3fcc980 100644 --- a/app/src/main/res/values-pl/preferences_values.xml +++ b/app/src/main/res/values-pl/preferences_values.xml @@ -41,4 +41,10 @@ Średnia ocen z 2 semestru Średnia ocen z całego roku + + + Nie pokazuj + Pokazuj wszystkie + Pokazuj pomniejszone + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 0972138f..5a1ac470 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -12,6 +12,7 @@ Ustawienia Więcej O aplikacji + Twórcy Licencje Wiadomości Nowa wiadomość @@ -66,10 +67,12 @@ Zmień semestr Brak ocen Waga + Waga: %s Komentarz Brak nowych ocen Ilość nowych ocen: %1$d Średnia: %1$.2f + Punkty: %s Brak średniej Przewidywana: %1$s Końcowa: %1$s @@ -139,6 +142,11 @@ %1$d nieobecności %1$d nieobecności + Powód nieobecności (opcjonalny) + Wyślij + Usprawiedliwiono pomyślnie! + Musisz wybrać co najmniej jedną nieobecność! + Usprawiedliw @@ -268,6 +276,8 @@ Wersja aplikacji + Twórcy + Lista programistów Wulkanowego Zgłoś błąd Wyślij zgłoszenie o błędzie poprzez e-maila FAQ @@ -286,6 +296,11 @@ Licencja + + Awatar + Zobacz więcej na GitHub + + Treść Ponów @@ -365,4 +380,5 @@ Wystąpił nieoczekiwany błąd Funkcja wyłączona przez szkołę Funkcja niedostępna w tym trybie + Pokazuj lekcje całej klasy diff --git a/app/src/main/res/values-ru/preferences_values.xml b/app/src/main/res/values-ru/preferences_values.xml index 641d754f..0e82c280 100644 --- a/app/src/main/res/values-ru/preferences_values.xml +++ b/app/src/main/res/values-ru/preferences_values.xml @@ -36,4 +36,10 @@ Средняя оценка со 2 семестра Средняя оценка с целого года + + + Не показывать + Показать все + Показать меньше + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0499cbf5..5698b35a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -72,6 +72,7 @@ Новые оценки отсутствуют Количество новых оценок: %1$d Средняя оценка: %1$.2f + точек: %s Средняя оценка отсутствует Ожидаемая оценка: %1$s Итоговая оценка: %1$s @@ -364,5 +365,6 @@ Произошла неожиданная ошибка Функция была выключена школой Функция не доступна в этом режиме + Показать уроки всего класса diff --git a/app/src/main/res/values/preferences_defaults.xml b/app/src/main/res/values/preferences_defaults.xml index 91b357d9..be31b440 100644 --- a/app/src/main/res/values/preferences_defaults.xml +++ b/app/src/main/res/values/preferences_defaults.xml @@ -16,4 +16,5 @@ 0.33 0.33 true + no diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml index a2172db9..49601b55 100644 --- a/app/src/main/res/values/preferences_keys.xml +++ b/app/src/main/res/values/preferences_keys.xml @@ -16,4 +16,5 @@ grade_modifier_plus grade_modifier_minus fill_message_content + show_whole_class_plan diff --git a/app/src/main/res/values/preferences_values.xml b/app/src/main/res/values/preferences_values.xml index ee54d53c..3b1fde65 100644 --- a/app/src/main/res/values/preferences_values.xml +++ b/app/src/main/res/values/preferences_values.xml @@ -90,4 +90,15 @@ only_one_semester all_year + + + No show + Show all + Show smaller + + + no + yes + small + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c8b94bff..3fa133c9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ Settings More About + Creators Licenses Messages New message @@ -68,10 +69,12 @@ Change semester No grades Weight + Weight: %s Comment No new grades Number of new ratings: %1$d Average: %1$.2f + Points: %s No average Predicted: %1$s Final: %1$s @@ -133,6 +136,11 @@ %1$d absence %1$d absences + Absence reason (optional) + Send + Absence excused successfully! + You must select at least one absence! + Excuse @@ -249,6 +257,8 @@ App version + Creators + List of Wulkanowy developers Report a bug Send a bug report via e-mail FAQ @@ -267,6 +277,11 @@ License + + Avatar + See more on GitHub + + Content Retry @@ -300,6 +315,7 @@ Show presence in attendance Application theme Expand grades + Show whole class lessons Grades color scheme App language diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 68c0800b..8f30e345 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -16,6 +16,7 @@