diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index cb2f41195..0ac66f649 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -18,18 +18,9 @@
-
-
-
-
-
-
-
+
-
-
-
@@ -143,13 +134,11 @@
+
-
-
-
diff --git a/app/build.gradle b/app/build.gradle
index 759be4107..6e7bb42d0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -131,14 +131,14 @@ play {
ext {
work_manager = "2.5.0"
- room = "2.2.6"
+ room = "2.3.0-alpha04"
chucker = "3.4.0"
mockk = "1.10.5"
moshi = "1.11.0"
}
dependencies {
- implementation "io.github.wulkanowy:sdk:a722e777"
+ implementation "io.github.wulkanowy:sdk:b7576e86"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
@@ -214,6 +214,7 @@ dependencies {
testImplementation "junit:junit:4.13.1"
testImplementation "io.mockk:mockk:$mockk"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
+ testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
androidTestImplementation "androidx.test:core:1.3.0"
androidTestImplementation "androidx.test:runner:1.3.0"
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/31.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/31.json
new file mode 100644
index 000000000..55a51e042
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/31.json
@@ -0,0 +1,2136 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 31,
+ "identityHash": "d642512ffa5fe81ae9308c9c55612539",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Students_email_symbol_student_id_school_id_class_id",
+ "unique": true,
+ "columnNames": [
+ "email",
+ "symbol",
+ "student_id",
+ "school_id",
+ "class_id"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Semesters",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "semester_id"
+ ],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timeId",
+ "columnName": "time_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presence",
+ "columnName": "presence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "exemption",
+ "columnName": "exemption",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lateness",
+ "columnName": "lateness",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excused",
+ "columnName": "excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deleted",
+ "columnName": "deleted",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excusable",
+ "columnName": "excusable",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excuseStatus",
+ "columnName": "excuse_status",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectId",
+ "columnName": "subject_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "month",
+ "columnName": "month",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presence",
+ "columnName": "presence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absenceExcused",
+ "columnName": "absence_excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absenceForSchoolReasons",
+ "columnName": "absence_for_school_reasons",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lateness",
+ "columnName": "lateness",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "latenessExcused",
+ "columnName": "lateness_excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "exemption",
+ "columnName": "exemption",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isRead",
+ "columnName": "is_read",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entry",
+ "columnName": "entry",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "value",
+ "columnName": "value",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "modifier",
+ "columnName": "modifier",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "comment",
+ "columnName": "comment",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "color",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gradeSymbol",
+ "columnName": "grade_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "weight",
+ "columnName": "weight",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "weightValue",
+ "columnName": "weightValue",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "position",
+ "columnName": "position",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGrade",
+ "columnName": "predicted_grade",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGrade",
+ "columnName": "final_grade",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "proposedPoints",
+ "columnName": "proposed_points",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalPoints",
+ "columnName": "final_points",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "pointsSum",
+ "columnName": "points_sum",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "average",
+ "columnName": "average",
+ "affinity": "REAL",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "others",
+ "columnName": "others",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "student",
+ "columnName": "student",
+ "affinity": "REAL",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "recipient",
+ "columnName": "recipient_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "removed",
+ "columnName": "removed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+ "fields": [
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "oneDriveId",
+ "columnName": "one_drive_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "real_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isRead",
+ "columnName": "is_read",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "luckyNumber",
+ "columnName": "lucky_number",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "topic",
+ "columnName": "topic",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "substitution",
+ "columnName": "substitution",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "resources",
+ "columnName": "resources",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ReportingUnits",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "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": "userLoginId",
+ "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": "userLoginId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceId",
+ "columnName": "device_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shortName",
+ "columnName": "short_name",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "contact",
+ "columnName": "contact",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "headmaster",
+ "columnName": "headmaster",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "pedagogue",
+ "columnName": "pedagogue",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `first_guardian_full_name` TEXT NOT NULL, `first_guardian_kinship` TEXT NOT NULL, `first_guardian_address` TEXT NOT NULL, `first_guardian_phones` TEXT NOT NULL, `first_guardian_email` TEXT NOT NULL, `second_guardian_full_name` TEXT NOT NULL, `second_guardian_kinship` TEXT NOT NULL, `second_guardian_address` TEXT NOT NULL, `second_guardian_phones` TEXT NOT NULL, `second_guardian_email` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "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, 'd642512ffa5fe81ae9308c9c55612539')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/TestDispatchersProvider.kt b/app/src/androidTest/java/io/github/wulkanowy/data/TestDispatchersProvider.kt
deleted file mode 100644
index 8c4354d9a..000000000
--- a/app/src/androidTest/java/io/github/wulkanowy/data/TestDispatchersProvider.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package io.github.wulkanowy.data
-
-import io.github.wulkanowy.utils.DispatchersProvider
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.Dispatchers
-
-class TestDispatchersProvider : DispatchersProvider() {
-
- override val backgroundThread: CoroutineDispatcher
- get() = Dispatchers.Unconfined
-}
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 324cb89fb..8b850659d 100644
--- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
@@ -33,17 +33,21 @@ internal class RepositoryModule {
setSimpleHttpLogger { Timber.d(it) }
// for debug only
- addInterceptor(ChuckerInterceptor.Builder(context)
- .collector(chuckerCollector)
- .alwaysReadResponseBody(true)
- .build(), network = true
+ addInterceptor(
+ ChuckerInterceptor.Builder(context)
+ .collector(chuckerCollector)
+ .alwaysReadResponseBody(true)
+ .build(), network = true
)
}
}
@Singleton
@Provides
- fun provideChuckerCollector(@ApplicationContext context: Context, prefRepository: PreferencesRepository): ChuckerCollector {
+ fun provideChuckerCollector(
+ @ApplicationContext context: Context,
+ prefRepository: PreferencesRepository
+ ): ChuckerCollector {
return ChuckerCollector(
context = context,
showNotification = prefRepository.isDebugNotificationEnable,
@@ -53,7 +57,10 @@ internal class RepositoryModule {
@Singleton
@Provides
- fun provideDatabase(@ApplicationContext context: Context, sharedPrefProvider: SharedPrefProvider) = AppDatabase.newInstance(context, sharedPrefProvider)
+ fun provideDatabase(
+ @ApplicationContext context: Context,
+ sharedPrefProvider: SharedPrefProvider,
+ ) = AppDatabase.newInstance(context, sharedPrefProvider)
@Singleton
@Provides
@@ -65,7 +72,8 @@ internal class RepositoryModule {
@Singleton
@Provides
- fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+ fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences =
+ PreferenceManager.getDefaultSharedPreferences(context)
@Singleton
@Provides
@@ -89,7 +97,8 @@ internal class RepositoryModule {
@Singleton
@Provides
- fun provideGradeSemesterStatisticsDao(database: AppDatabase) = database.gradeSemesterStatisticsDao
+ fun provideGradeSemesterStatisticsDao(database: AppDatabase) =
+ database.gradeSemesterStatisticsDao
@Singleton
@Provides
@@ -166,4 +175,8 @@ internal class RepositoryModule {
@Singleton
@Provides
fun provideTimetableAdditionalDao(database: AppDatabase) = database.timetableAdditionalDao
+
+ @Singleton
+ @Provides
+ fun provideStudentInfoDao(database: AppDatabase) = database.studentInfoDao
}
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 6b2301fc7..d1f99e3e8 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
@@ -28,6 +28,7 @@ import io.github.wulkanowy.data.db.dao.ReportingUnitDao
import io.github.wulkanowy.data.db.dao.SchoolDao
import io.github.wulkanowy.data.db.dao.SemesterDao
import io.github.wulkanowy.data.db.dao.StudentDao
+import io.github.wulkanowy.data.db.dao.StudentInfoDao
import io.github.wulkanowy.data.db.dao.SubjectDao
import io.github.wulkanowy.data.db.dao.TeacherDao
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
@@ -53,6 +54,7 @@ import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.db.entities.StudentInfo
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.data.db.entities.Timetable
@@ -80,6 +82,7 @@ import io.github.wulkanowy.data.db.migrations.Migration28
import io.github.wulkanowy.data.db.migrations.Migration29
import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration30
+import io.github.wulkanowy.data.db.migrations.Migration31
import io.github.wulkanowy.data.db.migrations.Migration4
import io.github.wulkanowy.data.db.migrations.Migration5
import io.github.wulkanowy.data.db.migrations.Migration6
@@ -116,6 +119,7 @@ import javax.inject.Singleton
School::class,
Conference::class,
TimetableAdditional::class,
+ StudentInfo::class,
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@@ -124,7 +128,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
- const val VERSION_SCHEMA = 30
+ const val VERSION_SCHEMA = 31
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array {
return arrayOf(
@@ -157,6 +161,7 @@ abstract class AppDatabase : RoomDatabase() {
Migration28(),
Migration29(),
Migration30(),
+ Migration31()
)
}
@@ -219,4 +224,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract val conferenceDao: ConferenceDao
abstract val timetableAdditionalDao: TimetableAdditionalDao
+
+ abstract val studentInfoDao: StudentInfoDao
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentInfoDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentInfoDao.kt
new file mode 100644
index 000000000..5ec86af12
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentInfoDao.kt
@@ -0,0 +1,15 @@
+package io.github.wulkanowy.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import io.github.wulkanowy.data.db.entities.StudentInfo
+import kotlinx.coroutines.flow.Flow
+import javax.inject.Singleton
+
+@Singleton
+@Dao
+interface StudentInfoDao : BaseDao {
+
+ @Query("SELECT * FROM StudentInfo WHERE student_id = :studentId")
+ fun loadStudentInfo(studentId: Int): Flow
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt
index b6c75ff8f..0b37b1e98 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Student.kt
@@ -7,7 +7,13 @@ import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.LocalDateTime
-@Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)])
+@Entity(
+ tableName = "Students",
+ indices = [Index(
+ value = ["email", "symbol", "student_id", "school_id", "class_id"],
+ unique = true
+ )]
+)
data class Student(
@ColumnInfo(name = "scrapper_base_url")
@@ -52,7 +58,7 @@ data class Student(
@ColumnInfo(name = "school_id")
val schoolSymbol: String,
- @ColumnInfo(name ="school_short")
+ @ColumnInfo(name = "school_short")
val schoolShortName: String,
@ColumnInfo(name = "school_name")
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentInfo.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentInfo.kt
new file mode 100644
index 000000000..70d00194f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/StudentInfo.kt
@@ -0,0 +1,85 @@
+package io.github.wulkanowy.data.db.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Embedded
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import io.github.wulkanowy.data.enums.Gender
+import java.io.Serializable
+import java.time.LocalDate
+
+@Entity(tableName = "StudentInfo")
+data class StudentInfo(
+
+ @ColumnInfo(name = "student_id")
+ val studentId: Int,
+
+ @ColumnInfo(name = "full_name")
+ val fullName: String,
+
+ @ColumnInfo(name = "first_name")
+ val firstName: String,
+
+ @ColumnInfo(name = "second_name")
+ val secondName: String,
+
+ val surname: String,
+
+ @ColumnInfo(name = "birth_date")
+ val birthDate: LocalDate,
+
+ @ColumnInfo(name = "birth_place")
+ val birthPlace: String,
+
+ val gender: Gender,
+
+ @ColumnInfo(name = "has_polish_citizenship")
+ val hasPolishCitizenship: Boolean,
+
+ @ColumnInfo(name = "family_name")
+ val familyName: String,
+
+ @ColumnInfo(name = "parents_names")
+ val parentsNames: String,
+
+ val address: String,
+
+ @ColumnInfo(name = "registered_address")
+ val registeredAddress: String,
+
+ @ColumnInfo(name = "correspondence_address")
+ val correspondenceAddress: String,
+
+ @ColumnInfo(name = "phone_number")
+ val phoneNumber: String,
+
+ @ColumnInfo(name = "cell_phone_number")
+ val cellPhoneNumber: String,
+
+ val email: String,
+
+ @Embedded(prefix = "first_guardian_")
+ val firstGuardian: StudentGuardian,
+
+ @Embedded(prefix = "second_guardian_")
+ val secondGuardian: StudentGuardian
+
+) : Serializable {
+
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0
+}
+
+data class StudentGuardian(
+
+ @ColumnInfo(name = "full_name")
+ val fullName: String,
+
+ val kinship: String,
+
+ val address: String,
+
+ val phones: String,
+
+ val email: String
+) : Serializable
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration31.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration31.kt
new file mode 100644
index 000000000..064a3e5bc
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration31.kt
@@ -0,0 +1,42 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration31 : Migration(30, 31) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL(
+ """CREATE TABLE IF NOT EXISTS StudentInfo (
+ id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ student_id INTEGER NOT NULL,
+ full_name TEXT NOT NULL,
+ first_name TEXT NOT NULL,
+ second_name TEXT NOT NULL,
+ surname TEXT NOT NULL,
+ birth_date INTEGER NOT NULL,
+ birth_place TEXT NOT NULL,
+ gender TEXT NOT NULL,
+ has_polish_citizenship INTEGER NOT NULL,
+ family_name TEXT NOT NULL,
+ parents_names TEXT NOT NULL,
+ address TEXT NOT NULL,
+ registered_address TEXT NOT NULL,
+ correspondence_address TEXT NOT NULL,
+ phone_number TEXT NOT NULL,
+ cell_phone_number TEXT NOT NULL,
+ email TEXT NOT NULL,
+ first_guardian_full_name TEXT NOT NULL,
+ first_guardian_kinship TEXT NOT NULL,
+ first_guardian_address TEXT NOT NULL,
+ first_guardian_phones TEXT NOT NULL,
+ first_guardian_email TEXT NOT NULL,
+ second_guardian_full_name TEXT NOT NULL,
+ second_guardian_kinship TEXT NOT NULL,
+ second_guardian_address TEXT NOT NULL,
+ second_guardian_phones TEXT NOT NULL,
+ second_guardian_email TEXT NOT NULL)
+ """
+ )
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/Gender.kt b/app/src/main/java/io/github/wulkanowy/data/enums/Gender.kt
new file mode 100644
index 000000000..df93dcbef
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/enums/Gender.kt
@@ -0,0 +1,3 @@
+package io.github.wulkanowy.data.enums
+
+enum class Gender { MALE, FEMALE }
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/StudentInfoMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentInfoMapper.kt
new file mode 100644
index 000000000..ebdf9de2e
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentInfoMapper.kt
@@ -0,0 +1,38 @@
+package io.github.wulkanowy.data.mappers
+
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.StudentGuardian
+import io.github.wulkanowy.data.db.entities.StudentInfo
+import io.github.wulkanowy.data.enums.Gender
+import io.github.wulkanowy.sdk.pojo.StudentGuardian as SdkStudentGuardian
+import io.github.wulkanowy.sdk.pojo.StudentInfo as SdkStudentInfo
+
+fun SdkStudentInfo.mapToEntity(semester: Semester) = StudentInfo(
+ studentId = semester.studentId,
+ fullName = fullName,
+ firstName = firstName,
+ secondName = secondName,
+ surname = surname,
+ birthDate = birthDate,
+ birthPlace = birthPlace,
+ gender = Gender.valueOf(gender.name),
+ hasPolishCitizenship = hasPolishCitizenship,
+ familyName = familyName,
+ parentsNames = parentsNames,
+ address = address,
+ registeredAddress = registeredAddress,
+ correspondenceAddress = correspondenceAddress,
+ phoneNumber = phoneNumber,
+ cellPhoneNumber = phoneNumber,
+ email = email,
+ firstGuardian = guardians[0].mapToEntity(),
+ secondGuardian = guardians[1].mapToEntity()
+)
+
+fun SdkStudentGuardian.mapToEntity() = StudentGuardian(
+ fullName = fullName,
+ kinship = kinship,
+ address = address,
+ phones = phones,
+ email = email
+)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
index 36d5c974a..6b22b32c3 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
@@ -16,16 +16,23 @@ class SchoolRepository @Inject constructor(
private val sdk: Sdk
) {
- fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
- shouldFetch = { it == null || forceRefresh },
- query = { schoolDb.load(semester.studentId, semester.classId) },
- fetch = { sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool().mapToEntity(semester) },
- saveFetchResult = { old, new ->
- if (new != old && old != null) {
- schoolDb.deleteAll(listOf(old))
- schoolDb.insertAll(listOf(new))
+ fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) =
+ networkBoundResource(
+ shouldFetch = { it == null || forceRefresh },
+ query = { schoolDb.load(semester.studentId, semester.classId) },
+ fetch = {
+ sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool()
+ .mapToEntity(semester)
+ },
+ saveFetchResult = { old, new ->
+ if (old != null && new != old) {
+ with(schoolDb) {
+ deleteAll(listOf(old))
+ insertAll(listOf(new))
+ }
+ } else if (old == null) {
+ schoolDb.insertAll(listOf(new))
+ }
}
- schoolDb.insertAll(listOf(new))
- }
- )
+ )
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
new file mode 100644
index 000000000..e3deb447f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
@@ -0,0 +1,38 @@
+package io.github.wulkanowy.data.repositories
+
+import io.github.wulkanowy.data.db.dao.StudentInfoDao
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.mappers.mapToEntity
+import io.github.wulkanowy.sdk.Sdk
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.networkBoundResource
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class StudentInfoRepository @Inject constructor(
+ private val studentInfoDao: StudentInfoDao,
+ private val sdk: Sdk
+) {
+
+ fun getStudentInfo(student: Student, semester: Semester, forceRefresh: Boolean) =
+ networkBoundResource(
+ shouldFetch = { it == null || forceRefresh },
+ query = { studentInfoDao.loadStudentInfo(student.studentId) },
+ fetch = {
+ sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ .getStudentInfo().mapToEntity(semester)
+ },
+ saveFetchResult = { old, new ->
+ if (old != null && new != old) {
+ with(studentInfoDao) {
+ deleteAll(listOf(old))
+ insertAll(listOf(new))
+ }
+ } else if (old == null) {
+ studentInfoDao.insertAll(listOf(new))
+ }
+ }
+ )
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRemote.kt
new file mode 100644
index 000000000..e69de29bb
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 0730033f1..c616c31ee 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
@@ -65,7 +65,11 @@ class AboutFragment : BaseFragment(R.layout.fragment_about
override val homepageRes: Triple?
get() = context?.run {
- Triple(getString(R.string.about_homepage), getString(R.string.about_homepage_summary), getCompatDrawable(R.drawable.ic_about_homepage))
+ Triple(
+ getString(R.string.about_homepage),
+ getString(R.string.about_homepage_summary),
+ getCompatDrawable(R.drawable.ic_all_home)
+ )
}
override val licensesRes: Triple?
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt
index 7ecb41399..4aa17073f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountAdapter.kt
@@ -8,16 +8,16 @@ import android.view.View.VISIBLE
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
-import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.HeaderAccountBinding
import io.github.wulkanowy.databinding.ItemAccountBinding
-import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject
class AccountAdapter @Inject constructor() : RecyclerView.Adapter() {
+ var isAccountQuickDialogMode = false
+
var items = emptyList>()
var onClickListener: (StudentWithSemesters) -> Unit = {}
@@ -30,54 +30,69 @@ class AccountAdapter @Inject constructor() : RecyclerView.Adapter HeaderViewHolder(HeaderAccountBinding.inflate(inflater, parent, false))
- AccountItem.ViewType.ITEM.id -> ItemViewHolder(ItemAccountBinding.inflate(inflater, parent, false))
+ AccountItem.ViewType.HEADER.id -> HeaderViewHolder(
+ HeaderAccountBinding.inflate(inflater, parent, false)
+ )
+ AccountItem.ViewType.ITEM.id -> ItemViewHolder(
+ ItemAccountBinding.inflate(inflater, parent, false)
+ )
else -> throw IllegalStateException()
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
- is HeaderViewHolder -> bindHeaderViewHolder(holder.binding, items[position].value as Account)
- is ItemViewHolder -> bindItemViewHolder(holder.binding, items[position].value as StudentWithSemesters)
+ is HeaderViewHolder -> bindHeaderViewHolder(
+ holder.binding,
+ items[position].value as Account,
+ position
+ )
+ is ItemViewHolder -> bindItemViewHolder(
+ holder.binding,
+ items[position].value as StudentWithSemesters
+ )
}
}
- private fun bindHeaderViewHolder(binding: HeaderAccountBinding, account: Account) {
+ private fun bindHeaderViewHolder(
+ binding: HeaderAccountBinding,
+ account: Account,
+ position: Int
+ ) {
with(binding) {
+ accountHeaderDivider.visibility = if (position == 0) GONE else VISIBLE
accountHeaderEmail.text = account.email
accountHeaderType.setText(if (account.isParent) R.string.account_type_parent else R.string.account_type_student)
}
}
@SuppressLint("SetTextI18n")
- private fun bindItemViewHolder(binding: ItemAccountBinding, studentWithSemesters: StudentWithSemesters) {
+ private fun bindItemViewHolder(
+ binding: ItemAccountBinding,
+ studentWithSemesters: StudentWithSemesters
+ ) {
val student = studentWithSemesters.student
val semesters = studentWithSemesters.semesters
val diary = semesters.maxByOrNull { it.semesterId }
+ val isDuplicatedStudent = items.filter {
+ if (it.value !is StudentWithSemesters) return@filter false
+ val studentToCompare = it.value.student
+
+ studentToCompare.studentId == student.studentId
+ && studentToCompare.schoolSymbol == student.schoolSymbol
+ && studentToCompare.symbol == student.symbol
+ }.size > 1 && isAccountQuickDialogMode
with(binding) {
accountItemName.text = "${student.studentName} ${diary?.diaryName.orEmpty()}"
accountItemSchool.text = studentWithSemesters.student.schoolName
- with(accountItemLoginMode) {
- visibility = when (Sdk.Mode.valueOf(student.loginMode)) {
- Sdk.Mode.API -> {
- setText(R.string.account_login_mobile_api)
- VISIBLE
- }
- Sdk.Mode.HYBRID -> {
- setText(R.string.account_login_hybrid)
- VISIBLE
- }
- Sdk.Mode.SCRAPPER -> {
- GONE
- }
- }
- }
+ accountItemAccountType.setText(if (student.isParent) R.string.account_type_parent else R.string.account_type_student)
+ accountItemAccountType.visibility = if (isDuplicatedStudent) VISIBLE else GONE
with(accountItemImage) {
- val colorImage = if (student.isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
- else context.getThemeAttrColor(R.attr.colorOnSurface, 153)
+ val colorImage =
+ if (student.isCurrent) context.getThemeAttrColor(R.attr.colorPrimary)
+ else context.getThemeAttrColor(R.attr.colorOnSurface, 153)
setColorFilter(colorImage, PorterDuff.Mode.SRC_IN)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt
deleted file mode 100644
index 1fa87268b..000000000
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountDialog.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-package io.github.wulkanowy.ui.modules.account
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Toast
-import android.widget.Toast.LENGTH_LONG
-import androidx.appcompat.app.AlertDialog
-import androidx.recyclerview.widget.LinearLayoutManager
-import dagger.hilt.android.AndroidEntryPoint
-import io.github.wulkanowy.R
-import io.github.wulkanowy.databinding.DialogAccountBinding
-import io.github.wulkanowy.ui.base.BaseDialogFragment
-import io.github.wulkanowy.ui.modules.login.LoginActivity
-import javax.inject.Inject
-
-@AndroidEntryPoint
-class AccountDialog : BaseDialogFragment(), AccountView {
-
- @Inject
- lateinit var presenter: AccountPresenter
-
- @Inject
- lateinit var accountAdapter: AccountAdapter
-
- companion object {
- fun newInstance() = AccountDialog()
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
- }
-
- override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
- return DialogAccountBinding.inflate(inflater).apply { binding = this }.root
- }
-
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- super.onActivityCreated(savedInstanceState)
- presenter.onAttachView(this)
- }
-
- override fun initView() {
- accountAdapter.onClickListener = presenter::onItemSelected
-
- with(binding) {
- accountDialogAdd.setOnClickListener { presenter.onAddSelected() }
- accountDialogRemove.setOnClickListener { presenter.onRemoveSelected() }
- accountDialogRecycler.apply {
- layoutManager = LinearLayoutManager(context)
- adapter = accountAdapter
- }
- }
- }
-
- override fun updateData(data: List>) {
- with(accountAdapter) {
- items = data
- notifyDataSetChanged()
- }
- }
-
- override fun showError(text: String, error: Throwable) {
- showMessage(text)
- }
-
- override fun showMessage(text: String) {
- Toast.makeText(context, text, LENGTH_LONG).show()
- }
-
- override fun dismissView() {
- dismiss()
- }
-
- override fun openLoginView() {
- activity?.let {
- startActivity(LoginActivity.getStartIntent(it))
- }
- }
-
- override fun showConfirmDialog() {
- context?.let {
- AlertDialog.Builder(it)
- .setTitle(R.string.account_logout_student)
- .setMessage(R.string.account_confirm)
- .setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() }
- .setNegativeButton(android.R.string.cancel) { _, _ -> }
- .show()
- }
- }
-
- override fun recreateMainView() {
- activity?.recreate()
- }
-
- override fun onDestroy() {
- presenter.onDetachView()
- super.onDestroy()
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt
new file mode 100644
index 000000000..7293527ee
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt
@@ -0,0 +1,111 @@
+package io.github.wulkanowy.ui.modules.account
+
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.View
+import androidx.core.view.get
+import androidx.recyclerview.widget.LinearLayoutManager
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.databinding.FragmentAccountBinding
+import io.github.wulkanowy.ui.base.BaseFragment
+import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
+import io.github.wulkanowy.ui.modules.login.LoginActivity
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import io.github.wulkanowy.ui.modules.main.MainView
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class AccountFragment : BaseFragment(R.layout.fragment_account),
+ AccountView, MainView.TitledView {
+
+ @Inject
+ lateinit var presenter: AccountPresenter
+
+ @Inject
+ lateinit var accountAdapter: AccountAdapter
+
+ companion object {
+
+ fun newInstance() = AccountFragment()
+ }
+
+ override val titleStringId = R.string.account_title
+
+ override var subtitleString = ""
+
+ override val isViewEmpty get() = accountAdapter.items.isEmpty()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setHasOptionsMenu(true)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding = FragmentAccountBinding.bind(view)
+ presenter.onAttachView(this)
+ }
+
+ override fun initView() {
+ binding.accountErrorRetry.setOnClickListener { presenter.onRetry() }
+ binding.accountErrorDetails.setOnClickListener { presenter.onDetailsClick() }
+
+ binding.accountRecycler.apply {
+ layoutManager = LinearLayoutManager(context)
+ adapter = accountAdapter
+ }
+
+ accountAdapter.onClickListener = presenter::onItemSelected
+
+ with(binding) {
+ accountAdd.setOnClickListener { presenter.onAddSelected() }
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ menu[0].isVisible = false
+ }
+
+ override fun updateData(data: List>) {
+ with(accountAdapter) {
+ items = data
+ notifyDataSetChanged()
+ }
+ }
+
+ override fun openLoginView() {
+ activity?.let {
+ startActivity(LoginActivity.getStartIntent(it))
+ }
+ }
+
+ override fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters) {
+ (activity as? MainActivity)?.pushView(
+ AccountDetailsFragment.newInstance(
+ studentWithSemesters
+ )
+ )
+ }
+
+ override fun showErrorView(show: Boolean) {
+ binding.accountError.visibility = if (show) View.VISIBLE else View.GONE
+ }
+
+ override fun setErrorDetails(message: String) {
+ binding.accountErrorMessage.text = message
+ }
+
+ override fun showProgress(show: Boolean) {
+ binding.accountProgress.visibility = if (show) View.VISIBLE else View.GONE
+ }
+
+ override fun showContent(show: Boolean) {
+ with(binding) {
+ accountRecycler.visibility = if (show) View.VISIBLE else View.GONE
+ accountAdd.visibility = if (show) View.VISIBLE else View.GONE
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
index be01f7c54..349561f82 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
@@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.account
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentRepository
-import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.afterLoading
@@ -15,101 +14,85 @@ import javax.inject.Inject
class AccountPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
- private val syncManager: SyncManager
) : BasePresenter(errorHandler, studentRepository) {
+ private lateinit var lastError: Throwable
+
override fun onAttachView(view: AccountView) {
super.onAttachView(view)
view.initView()
- Timber.i("Account dialog view was initialized")
+ Timber.i("Account view was initialized")
+ errorHandler.showErrorMessage = ::showErrorViewOnError
loadData()
}
+ fun onRetry() {
+ view?.run {
+ showErrorView(false)
+ showProgress(true)
+ }
+ loadData()
+ }
+
+ fun onDetailsClick() {
+ view?.showErrorDetailsDialog(lastError)
+ }
+
fun onAddSelected() {
Timber.i("Select add account")
view?.openLoginView()
}
- fun onRemoveSelected() {
- Timber.i("Select remove account")
- view?.showConfirmDialog()
- }
-
- fun onLogoutConfirm() {
- flowWithResource {
- val student = studentRepository.getCurrentStudent(false)
- studentRepository.logoutStudent(student)
-
- val students = studentRepository.getSavedStudents(false)
- if (students.isNotEmpty()) {
- studentRepository.switchStudent(students[0])
- }
- students
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Attempt to logout current user ")
- Status.SUCCESS -> view?.run {
- if (it.data!!.isEmpty()) {
- Timber.i("Logout result: Open login view")
- syncManager.stopSyncWorker()
- openClearLoginView()
- } else {
- Timber.i("Logout result: Switch to another student")
- recreateMainView()
- }
- }
- Status.ERROR -> {
- Timber.i("Logout result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
- }
- }.afterLoading {
- view?.dismissView()
- }.launch("logout")
- }
-
fun onItemSelected(studentWithSemesters: StudentWithSemesters) {
- Timber.i("Select student item ${studentWithSemesters.student.id}")
- if (studentWithSemesters.student.isCurrent) {
- view?.dismissView()
- } else flowWithResource { studentRepository.switchStudent(studentWithSemesters) }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Attempt to change a student")
- Status.SUCCESS -> {
- Timber.i("Change a student result: Success")
- view?.recreateMainView()
- }
- Status.ERROR -> {
- Timber.i("Change a student result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
- }
- }.afterLoading {
- view?.dismissView()
- }.launch("switch")
+ view?.openAccountDetailsView(studentWithSemesters)
}
private fun createAccountItems(items: List): List> {
- return items.groupBy { Account(it.student.email, it.student.isParent) }.map { (account, students) ->
- listOf(AccountItem(account, AccountItem.ViewType.HEADER)) + students.map { student ->
- AccountItem(student, AccountItem.ViewType.ITEM)
+ return items.groupBy {
+ Account("${it.student.userName} (${it.student.email})", it.student.isParent)
+ }
+ .map { (account, students) ->
+ listOf(
+ AccountItem(account, AccountItem.ViewType.HEADER)
+ ) + students.map { student ->
+ AccountItem(student, AccountItem.ViewType.ITEM)
+ }
}
- }.flatten()
+ .flatten()
}
private fun loadData() {
- flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading account data started")
- Status.SUCCESS -> {
- Timber.i("Loading account result: Success")
- view?.updateData(createAccountItems(it.data!!))
- }
- Status.ERROR -> {
- Timber.i("Loading account result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ flowWithResource { studentRepository.getSavedStudents() }
+ .onEach {
+ when (it.status) {
+ Status.LOADING -> Timber.i("Loading account data started")
+ Status.SUCCESS -> {
+ Timber.i("Loading account result: Success")
+ view?.updateData(createAccountItems(it.data!!))
+ view?.run {
+ showContent(true)
+ showErrorView(false)
+ }
+ }
+ Status.ERROR -> {
+ Timber.i("Loading account result: An exception occurred")
+ errorHandler.dispatch(it.error!!)
+ }
}
}
- }.launch()
+ .afterLoading { view?.showProgress(false) }
+ .launch()
+ }
+
+ private fun showErrorViewOnError(message: String, error: Throwable) {
+ view?.run {
+ if (isViewEmpty) {
+ lastError = error
+ setErrorDetails(message)
+ showErrorView(true)
+ showContent(false)
+ showProgress(false)
+ } else showError(message, error)
+ }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt
index a1f8086cd..3453909d1 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountView.kt
@@ -1,19 +1,26 @@
package io.github.wulkanowy.ui.modules.account
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
interface AccountView : BaseView {
+ val isViewEmpty: Boolean
+
fun initView()
fun updateData(data: List>)
- fun dismissView()
-
- fun showConfirmDialog()
-
fun openLoginView()
- fun recreateMainView()
+ fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters)
+
+ fun showErrorView(show: Boolean)
+
+ fun setErrorDetails(message: String)
+
+ fun showProgress(show: Boolean)
+
+ fun showContent(show: Boolean)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt
new file mode 100644
index 000000000..bdd4946ae
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt
@@ -0,0 +1,134 @@
+package io.github.wulkanowy.ui.modules.account.accountdetails
+
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.MenuItem
+import android.view.View
+import androidx.appcompat.app.AlertDialog
+import androidx.core.view.get
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.databinding.FragmentAccountDetailsBinding
+import io.github.wulkanowy.ui.base.BaseFragment
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import io.github.wulkanowy.ui.modules.main.MainView
+import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment
+import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class AccountDetailsFragment :
+ BaseFragment(R.layout.fragment_account_details),
+ AccountDetailsView, MainView.TitledView {
+
+ @Inject
+ lateinit var presenter: AccountDetailsPresenter
+
+ override val titleStringId = R.string.account_details_title
+
+ override var subtitleString = ""
+
+ companion object {
+
+ private const val ARGUMENT_KEY = "Data"
+
+ fun newInstance(studentWithSemesters: StudentWithSemesters) =
+ AccountDetailsFragment().apply {
+ arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, studentWithSemesters) }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setHasOptionsMenu(true)
+ arguments?.let {
+ presenter.studentWithSemesters =
+ it.getSerializable(ARGUMENT_KEY) as StudentWithSemesters
+ }
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding = FragmentAccountDetailsBinding.bind(view)
+ presenter.onAttachView(this)
+ }
+
+ override fun initView() {
+ binding.accountDetailsLogout.setOnClickListener { presenter.onRemoveSelected() }
+ binding.accountDetailsSelect.setOnClickListener { presenter.onStudentSelect() }
+ binding.accountDetailsSelect.isEnabled = !presenter.studentWithSemesters.student.isCurrent
+
+ binding.accountDetailsPersonalData.setOnClickListener {
+ presenter.onStudentInfoSelected(StudentInfoView.Type.PERSONAL)
+ }
+ binding.accountDetailsAddressData.setOnClickListener {
+ presenter.onStudentInfoSelected(StudentInfoView.Type.ADDRESS)
+ }
+ binding.accountDetailsContactData.setOnClickListener {
+ presenter.onStudentInfoSelected(StudentInfoView.Type.CONTACT)
+ }
+ binding.accountDetailsFamilyData.setOnClickListener {
+ presenter.onStudentInfoSelected(StudentInfoView.Type.FAMILY)
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ menu[0].isVisible = false
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ return if (item.itemId == R.id.accountDetailsMenuEdit) {
+ showAccountEditDetailsDialog()
+ return true
+ } else false
+ }
+
+ override fun showAccountData(studentWithSemesters: StudentWithSemesters) {
+ with(binding) {
+ accountDetailsName.text = studentWithSemesters.student.studentName
+ accountDetailsSchool.text = studentWithSemesters.student.schoolName
+ }
+ }
+
+ override fun showAccountEditDetailsDialog() {
+ (requireActivity() as MainActivity).showDialogFragment(AccountEditDetailsDialog.newInstance())
+ }
+
+ override fun showLogoutConfirmDialog() {
+ context?.let {
+ AlertDialog.Builder(it)
+ .setTitle(R.string.account_logout_student)
+ .setMessage(R.string.account_confirm)
+ .setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() }
+ .setNegativeButton(android.R.string.cancel) { _, _ -> }
+ .show()
+ }
+ }
+
+ override fun popView() {
+ (requireActivity() as MainActivity).popView(2)
+ }
+
+ override fun recreateMainView() {
+ requireActivity().recreate()
+ }
+
+ override fun openStudentInfoView(
+ infoType: StudentInfoView.Type,
+ studentWithSemesters: StudentWithSemesters
+ ) {
+ (requireActivity() as MainActivity).pushView(
+ StudentInfoFragment.newInstance(
+ infoType,
+ studentWithSemesters
+ )
+ )
+ }
+
+ override fun onDestroyView() {
+ presenter.onDetachView()
+ super.onDestroyView()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt
new file mode 100644
index 000000000..da437429e
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt
@@ -0,0 +1,100 @@
+package io.github.wulkanowy.ui.modules.account.accountdetails
+
+import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.services.sync.SyncManager
+import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.base.ErrorHandler
+import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
+import io.github.wulkanowy.utils.afterLoading
+import io.github.wulkanowy.utils.flowWithResource
+import kotlinx.coroutines.flow.onEach
+import timber.log.Timber
+import javax.inject.Inject
+
+class AccountDetailsPresenter @Inject constructor(
+ errorHandler: ErrorHandler,
+ studentRepository: StudentRepository,
+ private val syncManager: SyncManager
+) : BasePresenter(errorHandler, studentRepository) {
+
+ lateinit var studentWithSemesters: StudentWithSemesters
+
+ override fun onAttachView(view: AccountDetailsView) {
+ super.onAttachView(view)
+ view.initView()
+ Timber.i("Account details view was initialized")
+
+ view.showAccountData(studentWithSemesters)
+ }
+
+ fun onStudentInfoSelected(infoType: StudentInfoView.Type) {
+ view?.openStudentInfoView(infoType, studentWithSemesters)
+ }
+
+ fun onStudentSelect() {
+ Timber.i("Select student ${studentWithSemesters.student.id}")
+
+ flowWithResource { studentRepository.switchStudent(studentWithSemesters) }
+ .onEach {
+ when (it.status) {
+ Status.LOADING -> Timber.i("Attempt to change a student")
+ Status.SUCCESS -> {
+ Timber.i("Change a student result: Success")
+ view?.recreateMainView()
+ }
+ Status.ERROR -> {
+ Timber.i("Change a student result: An exception occurred")
+ errorHandler.dispatch(it.error!!)
+ }
+ }
+ }.afterLoading {
+ view?.popView()
+ }.launch("switch")
+ }
+
+ fun onRemoveSelected() {
+ Timber.i("Select remove account")
+ view?.showLogoutConfirmDialog()
+ }
+
+ fun onLogoutConfirm() {
+ flowWithResource {
+ val studentToLogout = studentWithSemesters.student
+
+ studentRepository.logoutStudent(studentToLogout)
+ val students = studentRepository.getSavedStudents(false)
+
+ if (studentToLogout.isCurrent && students.isNotEmpty()) {
+ studentRepository.switchStudent(students[0])
+ }
+
+ return@flowWithResource students
+ }.onEach {
+ when (it.status) {
+ Status.LOADING -> Timber.i("Attempt to logout user")
+ Status.SUCCESS -> view?.run {
+ when {
+ it.data!!.isEmpty() -> {
+ Timber.i("Logout result: Open login view")
+ syncManager.stopSyncWorker()
+ openClearLoginView()
+ }
+ studentWithSemesters.student.isCurrent -> {
+ Timber.i("Logout result: Logout student and switch to another")
+ recreateMainView()
+ }
+ else -> Timber.i("Logout result: Logout student")
+ }
+ }
+ Status.ERROR -> {
+ Timber.i("Logout result: An exception occurred")
+ errorHandler.dispatch(it.error!!)
+ }
+ }
+ }.afterLoading {
+ view?.popView()
+ }.launch("logout")
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsView.kt
new file mode 100644
index 000000000..f1001fd78
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsView.kt
@@ -0,0 +1,25 @@
+package io.github.wulkanowy.ui.modules.account.accountdetails
+
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.ui.base.BaseView
+import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
+
+interface AccountDetailsView : BaseView {
+
+ fun initView()
+
+ fun showAccountData(studentWithSemesters: StudentWithSemesters)
+
+ fun showAccountEditDetailsDialog()
+
+ fun showLogoutConfirmDialog()
+
+ fun popView()
+
+ fun recreateMainView()
+
+ fun openStudentInfoView(
+ infoType: StudentInfoView.Type,
+ studentWithSemesters: StudentWithSemesters
+ )
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountEditDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountEditDetailsDialog.kt
new file mode 100644
index 000000000..be0af7df1
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountEditDetailsDialog.kt
@@ -0,0 +1,38 @@
+package io.github.wulkanowy.ui.modules.account.accountdetails
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.DialogFragment
+import io.github.wulkanowy.databinding.DialogAccountEditDetailsBinding
+import io.github.wulkanowy.utils.lifecycleAwareVariable
+
+class AccountEditDetailsDialog : DialogFragment() {
+
+ private var binding: DialogAccountEditDetailsBinding by lifecycleAwareVariable()
+
+ companion object {
+
+ fun newInstance() = AccountEditDetailsDialog()
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setStyle(STYLE_NO_TITLE, 0)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return DialogAccountEditDetailsBinding.inflate(inflater).apply { binding = this }.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.accountEditDetailsCancel.setOnClickListener { dismiss() }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt
new file mode 100644
index 000000000..cb64a8fd3
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickDialog.kt
@@ -0,0 +1,82 @@
+package io.github.wulkanowy.ui.modules.account.accountquick
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.LinearLayoutManager
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.databinding.DialogAccountQuickBinding
+import io.github.wulkanowy.ui.base.BaseDialogFragment
+import io.github.wulkanowy.ui.modules.account.AccountAdapter
+import io.github.wulkanowy.ui.modules.account.AccountFragment
+import io.github.wulkanowy.ui.modules.account.AccountItem
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class AccountQuickDialog : BaseDialogFragment(), AccountQuickView {
+
+ @Inject
+ lateinit var accountAdapter: AccountAdapter
+
+ @Inject
+ lateinit var presenter: AccountQuickPresenter
+
+ companion object {
+ fun newInstance() = AccountQuickDialog()
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setStyle(STYLE_NO_TITLE, 0)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogAccountQuickBinding.inflate(inflater).apply { binding = this }.root
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ presenter.onAttachView(this)
+ }
+
+ override fun initView() {
+ binding.accountQuickDialogManger.setOnClickListener { presenter.onManagerSelected() }
+
+ with(accountAdapter) {
+ isAccountQuickDialogMode = true
+ onClickListener = presenter::onStudentSelect
+ }
+
+ with(binding.accountQuickDialogRecycler) {
+ layoutManager = LinearLayoutManager(context)
+ adapter = accountAdapter
+ }
+ }
+
+ override fun updateData(data: List>) {
+ with(accountAdapter) {
+ items = data
+ notifyDataSetChanged()
+ }
+ }
+
+ override fun popView() {
+ dismiss()
+ }
+
+ override fun recreateMainView() {
+ activity?.recreate()
+ }
+
+ override fun openAccountView() {
+ (requireActivity() as MainActivity).pushView(AccountFragment.newInstance())
+ }
+
+ override fun onDestroyView() {
+ presenter.onDetachView()
+ super.onDestroyView()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt
new file mode 100644
index 000000000..87b14c53f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt
@@ -0,0 +1,79 @@
+package io.github.wulkanowy.ui.modules.account.accountquick
+
+import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.base.ErrorHandler
+import io.github.wulkanowy.ui.modules.account.AccountItem
+import io.github.wulkanowy.utils.afterLoading
+import io.github.wulkanowy.utils.flowWithResource
+import kotlinx.coroutines.flow.onEach
+import timber.log.Timber
+import javax.inject.Inject
+
+class AccountQuickPresenter @Inject constructor(
+ errorHandler: ErrorHandler,
+ studentRepository: StudentRepository
+) : BasePresenter(errorHandler, studentRepository) {
+
+ override fun onAttachView(view: AccountQuickView) {
+ super.onAttachView(view)
+ view.initView()
+ Timber.i("Account quick dialog view was initialized")
+ loadData()
+ }
+
+ fun onManagerSelected() {
+ view?.run {
+ openAccountView()
+ popView()
+ }
+ }
+
+ fun onStudentSelect(studentWithSemesters: StudentWithSemesters) {
+ Timber.i("Select student ${studentWithSemesters.student.id}")
+
+ if (studentWithSemesters.student.isCurrent) {
+ view?.popView()
+ return
+ }
+
+ flowWithResource { studentRepository.switchStudent(studentWithSemesters) }
+ .onEach {
+ when (it.status) {
+ Status.LOADING -> Timber.i("Attempt to change a student")
+ Status.SUCCESS -> {
+ Timber.i("Change a student result: Success")
+ view?.recreateMainView()
+ }
+ Status.ERROR -> {
+ Timber.i("Change a student result: An exception occurred")
+ errorHandler.dispatch(it.error!!)
+ }
+ }
+ }.afterLoading {
+ view?.popView()
+ }.launch("switch")
+ }
+
+ private fun loadData() {
+ flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
+ when (it.status) {
+ Status.LOADING -> Timber.i("Loading account data started")
+ Status.SUCCESS -> {
+ Timber.i("Loading account result: Success")
+ view?.updateData(createAccountItems(it.data!!))
+ }
+ Status.ERROR -> {
+ Timber.i("Loading account result: An exception occurred")
+ errorHandler.dispatch(it.error!!)
+ }
+ }
+ }.launch()
+ }
+
+ private fun createAccountItems(items: List) = items.map {
+ AccountItem(it, AccountItem.ViewType.ITEM)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickView.kt
new file mode 100644
index 000000000..4a9420d99
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickView.kt
@@ -0,0 +1,17 @@
+package io.github.wulkanowy.ui.modules.account.accountquick
+
+import io.github.wulkanowy.ui.base.BaseView
+import io.github.wulkanowy.ui.modules.account.AccountItem
+
+interface AccountQuickView : BaseView {
+
+ fun initView()
+
+ fun updateData(data: List>)
+
+ fun recreateMainView()
+
+ fun popView()
+
+ fun openAccountView()
+}
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 83ed163fe..3bef3dc65 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
@@ -60,6 +60,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag
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
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 25b41aab5..5d93c5944 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
@@ -14,6 +14,7 @@ import android.os.Build.VERSION_CODES.LOLLIPOP
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
+import android.view.View
import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService
import androidx.core.view.ViewCompat
@@ -28,7 +29,7 @@ import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.ActivityMainBinding
import io.github.wulkanowy.ui.base.BaseActivity
-import io.github.wulkanowy.ui.modules.account.AccountDialog
+import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
@@ -65,17 +66,19 @@ class MainActivity : BaseActivity(), MainVie
private val overlayProvider by lazy { ElevationOverlayProvider(this) }
- private val navController = FragNavController(supportFragmentManager, R.id.mainFragmentContainer)
+ private val navController =
+ FragNavController(supportFragmentManager, R.id.mainFragmentContainer)
companion object {
const val EXTRA_START_MENU = "extraStartMenu"
- fun getStartIntent(context: Context, startMenu: MainView.Section? = null, clear: Boolean = false): Intent {
- return Intent(context, MainActivity::class.java)
- .apply {
- if (clear) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
- startMenu?.let { putExtra(EXTRA_START_MENU, it.id) }
- }
+ fun getStartIntent(
+ context: Context,
+ startMenu: MainView.Section? = null,
+ clear: Boolean = false
+ ) = Intent(context, MainActivity::class.java).apply {
+ if (clear) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
+ startMenu?.let { putExtra(EXTRA_START_MENU, it.id) }
}
}
@@ -83,7 +86,10 @@ class MainActivity : BaseActivity(), MainVie
override val currentStackSize get() = navController.currentStack?.size
- override val currentViewTitle get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let { getString(it) }
+ override val currentViewTitle
+ get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let {
+ getString(it)
+ }
override val currentViewSubtitle get() = (navController.currentFrag as? MainView.TitledView)?.subtitleString
@@ -106,7 +112,10 @@ class MainActivity : BaseActivity(), MainVie
messageContainer = binding.mainFragmentContainer
updateHelper.messageContainer = binding.mainFragmentContainer
- presenter.onAttachView(this, MainView.Section.values().singleOrNull { it.id == intent.getIntExtra(EXTRA_START_MENU, -1) })
+ val section = MainView.Section.values()
+ .singleOrNull { it.id == intent.getIntExtra(EXTRA_START_MENU, -1) }
+
+ presenter.onAttachView(this, section)
with(navController) {
initialize(startMenuIndex, savedInstanceState)
@@ -132,21 +141,49 @@ class MainActivity : BaseActivity(), MainVie
val shortcutsList = mutableListOf()
listOf(
- Triple(getString(R.string.grade_title), R.drawable.ic_shortcut_grade, MainView.Section.GRADE),
- Triple(getString(R.string.attendance_title), R.drawable.ic_shortcut_attendance, MainView.Section.ATTENDANCE),
- Triple(getString(R.string.exam_title), R.drawable.ic_shortcut_exam, MainView.Section.EXAM),
- Triple(getString(R.string.timetable_title), R.drawable.ic_shortcut_timetable, MainView.Section.TIMETABLE),
- Triple(getString(R.string.message_title), R.drawable.ic_shortcut_message, MainView.Section.MESSAGE)
+ Triple(
+ getString(R.string.grade_title),
+ R.drawable.ic_shortcut_grade,
+ MainView.Section.GRADE
+ ),
+ Triple(
+ getString(R.string.attendance_title),
+ R.drawable.ic_shortcut_attendance,
+ MainView.Section.ATTENDANCE
+ ),
+ Triple(
+ getString(R.string.exam_title),
+ R.drawable.ic_shortcut_exam,
+ MainView.Section.EXAM
+ ),
+ Triple(
+ getString(R.string.timetable_title),
+ R.drawable.ic_shortcut_timetable,
+ MainView.Section.TIMETABLE
+ ),
+ Triple(
+ getString(R.string.message_title),
+ R.drawable.ic_shortcut_message,
+ MainView.Section.MESSAGE
+ )
).forEach { (title, icon, enum) ->
- shortcutsList.add(ShortcutInfo.Builder(applicationContext, title)
- .setShortLabel(title)
- .setLongLabel(title)
- .setIcon(Icon.createWithResource(applicationContext, icon))
- .setIntents(arrayOf(
- Intent(applicationContext, MainActivity::class.java).setAction(Intent.ACTION_VIEW),
- Intent(applicationContext, MainActivity::class.java).putExtra(EXTRA_START_MENU, enum.id)
- .setAction(Intent.ACTION_VIEW).addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK)))
- .build())
+ shortcutsList.add(
+ ShortcutInfo.Builder(applicationContext, title)
+ .setShortLabel(title)
+ .setLongLabel(title)
+ .setIcon(Icon.createWithResource(applicationContext, icon))
+ .setIntents(
+ arrayOf(
+ Intent(applicationContext, MainActivity::class.java)
+ .setAction(Intent.ACTION_VIEW),
+ Intent(applicationContext, MainActivity::class.java)
+ .putExtra(EXTRA_START_MENU, enum.id)
+ .setAction(Intent.ACTION_VIEW)
+ .addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK)
+ )
+ )
+ .build()
+ )
}
getSystemService()?.dynamicShortcuts = shortcutsList
@@ -160,20 +197,33 @@ class MainActivity : BaseActivity(), MainVie
override fun initView() {
with(binding.mainToolbar) {
if (SDK_INT >= LOLLIPOP) stateListAnimator = null
- setBackgroundColor(overlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(4f)))
+ setBackgroundColor(
+ overlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(4f))
+ )
}
with(binding.mainBottomNav) {
- addItems(listOf(
- AHBottomNavigationItem(R.string.grade_title, R.drawable.ic_main_grade, 0),
- AHBottomNavigationItem(R.string.attendance_title, R.drawable.ic_main_attendance, 0),
- AHBottomNavigationItem(R.string.exam_title, R.drawable.ic_main_exam, 0),
- AHBottomNavigationItem(R.string.timetable_title, R.drawable.ic_main_timetable, 0),
- AHBottomNavigationItem(R.string.more_title, R.drawable.ic_main_more, 0)
- ))
+ addItems(
+ listOf(
+ AHBottomNavigationItem(R.string.grade_title, R.drawable.ic_main_grade, 0),
+ AHBottomNavigationItem(
+ R.string.attendance_title,
+ R.drawable.ic_main_attendance,
+ 0
+ ),
+ AHBottomNavigationItem(R.string.exam_title, R.drawable.ic_main_exam, 0),
+ AHBottomNavigationItem(
+ R.string.timetable_title,
+ R.drawable.ic_main_timetable,
+ 0
+ ),
+ AHBottomNavigationItem(R.string.more_title, R.drawable.ic_main_more, 0)
+ )
+ )
accentColor = getThemeAttrColor(R.attr.colorPrimary)
inactiveColor = getThemeAttrColor(R.attr.colorOnSurface, 153)
- defaultBackgroundColor = overlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(8f))
+ defaultBackgroundColor =
+ overlayProvider.compositeOverlayWithThemeSurfaceColorIfNeeded(dpToPx(8f))
titleState = ALWAYS_SHOW
currentItem = startMenuIndex
isBehaviorTranslationEnabled = false
@@ -183,6 +233,13 @@ class MainActivity : BaseActivity(), MainVie
with(navController) {
setOnViewChangeListener { section, name ->
+ binding.mainBottomNav.visibility =
+ if (section == MainView.Section.ACCOUNT || section == MainView.Section.STUDENT_INFO) {
+ View.GONE
+ } else {
+ View.VISIBLE
+ }
+
analytics.setCurrentScreen(this@MainActivity, name)
presenter.onViewChange(section)
}
@@ -224,7 +281,7 @@ class MainActivity : BaseActivity(), MainVie
}
override fun showAccountPicker() {
- navController.showDialogFragment(AccountDialog.newInstance())
+ navController.showDialogFragment(AccountQuickDialog.newInstance())
}
override fun showActionBarElevation(show: Boolean) {
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 97b556e3e..7f4098147 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
@@ -64,6 +64,8 @@ interface MainView : BaseView {
LUCKY_NUMBER(8),
SETTINGS(9),
ABOUT(10),
- SCHOOL(11)
+ SCHOOL(11),
+ ACCOUNT(12),
+ STUDENT_INFO(13)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt
index 491a19ec7..202d4e5d7 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt
@@ -81,10 +81,7 @@ class SchoolPresenter @Inject constructor(
showEmpty(false)
showErrorView(false)
}
- analytics.logEvent(
- "load_item",
- "type" to "school"
- )
+ analytics.logEvent("load_item", "type" to "school")
} else view?.run {
Timber.i("Loading school result: No school info found")
showContent(!isViewEmpty)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt
new file mode 100644
index 000000000..602ec07ad
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoAdapter.kt
@@ -0,0 +1,42 @@
+package io.github.wulkanowy.ui.modules.studentinfo
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import io.github.wulkanowy.databinding.ItemStudentInfoBinding
+import javax.inject.Inject
+
+class StudentInfoAdapter @Inject constructor() :
+ RecyclerView.Adapter() {
+
+ var items = listOf>()
+
+ var onItemClickListener: (position: Int) -> Unit = {}
+
+ var onItemLongClickListener: (text: String) -> Unit = {}
+
+ override fun getItemCount() = items.size
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
+ ItemStudentInfoBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ )
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val item = items[position]
+
+ with(holder.binding) {
+ studentInfoItemTitle.text = item.first
+ studentInfoItemSubtitle.text = item.second
+
+ with(root) {
+ setOnClickListener { onItemClickListener(position) }
+ setOnLongClickListener {
+ onItemLongClickListener(studentInfoItemSubtitle.text.toString())
+ true
+ }
+ }
+ }
+ }
+
+ class ViewHolder(val binding: ItemStudentInfoBinding) : RecyclerView.ViewHolder(binding.root)
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt
new file mode 100644
index 000000000..0075d558f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt
@@ -0,0 +1,228 @@
+package io.github.wulkanowy.ui.modules.studentinfo
+
+import android.annotation.SuppressLint
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.os.Bundle
+import android.view.Menu
+import android.view.MenuInflater
+import android.view.View
+import android.widget.Toast
+import androidx.core.content.getSystemService
+import androidx.core.view.get
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.StudentInfo
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.data.enums.Gender
+import io.github.wulkanowy.databinding.FragmentStudentInfoBinding
+import io.github.wulkanowy.ui.base.BaseFragment
+import io.github.wulkanowy.ui.modules.main.MainActivity
+import io.github.wulkanowy.ui.modules.main.MainView
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class StudentInfoFragment :
+ BaseFragment(R.layout.fragment_student_info), StudentInfoView,
+ MainView.TitledView {
+
+ @Inject
+ lateinit var presenter: StudentInfoPresenter
+
+ @Inject
+ lateinit var studentInfoAdapter: StudentInfoAdapter
+
+ override val titleStringId: Int
+ get() = R.string.student_info_title
+
+ override val isViewEmpty get() = studentInfoAdapter.items.isEmpty()
+
+ companion object {
+
+ private const val INFO_TYPE_ARGUMENT_KEY = "info_type"
+
+ private const val STUDENT_ARGUMENT_KEY = "student_with_semesters"
+
+ fun newInstance(type: StudentInfoView.Type, studentWithSemesters: StudentWithSemesters) =
+ StudentInfoFragment().apply {
+ arguments = Bundle().apply {
+ putSerializable(INFO_TYPE_ARGUMENT_KEY, type)
+ putSerializable(STUDENT_ARGUMENT_KEY, studentWithSemesters)
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setHasOptionsMenu(true)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding = FragmentStudentInfoBinding.bind(view)
+ presenter.onAttachView(
+ this,
+ requireArguments().getSerializable(INFO_TYPE_ARGUMENT_KEY) as StudentInfoView.Type,
+ requireArguments().getSerializable(STUDENT_ARGUMENT_KEY) as StudentWithSemesters
+ )
+ }
+
+ override fun initView() {
+ with(binding) {
+ studentInfoSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
+ studentInfoErrorRetry.setOnClickListener { presenter.onRetry() }
+ studentInfoErrorDetails.setOnClickListener { presenter.onDetailsClick() }
+ }
+
+ with(studentInfoAdapter) {
+ onItemClickListener = presenter::onItemSelected
+ onItemLongClickListener = presenter::onItemLongClick
+ }
+
+ with(binding.studentInfoRecycler) {
+ layoutManager = LinearLayoutManager(context)
+ addItemDecoration(DividerItemDecoration(context, RecyclerView.VERTICAL))
+ setHasFixedSize(true)
+ adapter = studentInfoAdapter
+ }
+ }
+
+ override fun updateData(data: List>) {
+ with(studentInfoAdapter) {
+ items = data
+ notifyDataSetChanged()
+ }
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ menu[0].isVisible = false
+ }
+
+ override fun showPersonalTypeData(studentInfo: StudentInfo) {
+ updateData(
+ listOf(
+ getString(R.string.student_info_first_name) to studentInfo.firstName,
+ getString(R.string.student_info_second_name) to studentInfo.secondName,
+ getString(R.string.student_info_gender) to getString(if (studentInfo.gender == Gender.MALE) R.string.student_info_male else R.string.student_info_female),
+ getString(R.string.student_info_polish_citizenship) to getString(if (studentInfo.hasPolishCitizenship) R.string.all_yes else R.string.all_no),
+ getString(R.string.student_info_family_name) to studentInfo.familyName,
+ getString(R.string.student_info_parents_name) to studentInfo.parentsNames
+ ).map {
+ if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
+ }
+ )
+ }
+
+ override fun showContactTypeData(studentInfo: StudentInfo) {
+ updateData(
+ listOf(
+ getString(R.string.student_info_phone) to studentInfo.phoneNumber,
+ getString(R.string.student_info_cellphone) to studentInfo.cellPhoneNumber,
+ getString(R.string.student_info_email) to studentInfo.email
+ ).map {
+ if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
+ }
+ )
+ }
+
+ @SuppressLint("DefaultLocale")
+ override fun showFamilyTypeData(studentInfo: StudentInfo) {
+ updateData(
+ listOf(
+ studentInfo.firstGuardian.kinship.capitalize() to studentInfo.firstGuardian.fullName,
+ studentInfo.secondGuardian.kinship.capitalize() to studentInfo.secondGuardian.fullName
+ ).map {
+ if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
+ }
+ )
+ }
+
+ override fun showAddressTypeData(studentInfo: StudentInfo) {
+ updateData(
+ listOf(
+ getString(R.string.student_info_address) to studentInfo.address,
+ getString(R.string.student_info_registered_address) to studentInfo.registeredAddress,
+ getString(R.string.student_info_correspondence_address) to studentInfo.correspondenceAddress
+ ).map {
+ if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
+ }
+ )
+ }
+
+ override fun showFirstGuardianTypeData(studentInfo: StudentInfo) {
+ updateData(
+ listOf(
+ getString(R.string.student_info_full_name) to studentInfo.firstGuardian.fullName,
+ getString(R.string.student_info_kinship) to studentInfo.firstGuardian.kinship,
+ getString(R.string.student_info_guardian_address) to studentInfo.firstGuardian.address,
+ getString(R.string.student_info_phones) to studentInfo.firstGuardian.phones,
+ getString(R.string.student_info_email) to studentInfo.firstGuardian.email
+ ).map {
+ if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
+ }
+ )
+ }
+
+ override fun showSecondGuardianTypeData(studentInfo: StudentInfo) {
+ updateData(
+ listOf(
+ getString(R.string.student_info_full_name) to studentInfo.secondGuardian.fullName,
+ getString(R.string.student_info_kinship) to studentInfo.secondGuardian.kinship,
+ getString(R.string.student_info_guardian_address) to studentInfo.secondGuardian.address,
+ getString(R.string.student_info_phones) to studentInfo.secondGuardian.phones,
+ getString(R.string.student_info_email) to studentInfo.secondGuardian.email
+ ).map {
+ if (it.second.isBlank()) it.copy(second = getString(R.string.all_no_data)) else it
+ }
+ )
+ }
+
+ override fun openStudentInfoView(
+ infoType: StudentInfoView.Type,
+ studentWithSemesters: StudentWithSemesters
+ ) {
+ (requireActivity() as MainActivity).pushView(newInstance(infoType, studentWithSemesters))
+ }
+
+ override fun showEmpty(show: Boolean) {
+ binding.studentInfoEmpty.visibility = if (show) View.VISIBLE else View.GONE
+ }
+
+ override fun showErrorView(show: Boolean) {
+ binding.studentInfoError.visibility = if (show) View.VISIBLE else View.GONE
+ }
+
+ override fun setErrorDetails(message: String) {
+ binding.studentInfoErrorMessage.text = message
+ }
+
+ override fun showProgress(show: Boolean) {
+ binding.studentInfoProgress.visibility = if (show) View.VISIBLE else View.GONE
+ }
+
+ override fun enableSwipe(enable: Boolean) {
+ binding.studentInfoSwipe.isEnabled = enable
+ }
+
+ override fun showContent(show: Boolean) {
+ binding.studentInfoRecycler.visibility = if (show) View.VISIBLE else View.GONE
+ }
+
+ override fun hideRefresh() {
+ binding.studentInfoSwipe.isRefreshing = false
+ }
+
+ override fun copyToClipboard(text: String) {
+ val clipData = ClipData.newPlainText("student_info_wulkanowy", text)
+ requireActivity().getSystemService()?.setPrimaryClip(clipData)
+ Toast.makeText(context, R.string.all_copied, Toast.LENGTH_SHORT).show()
+ }
+
+ override fun onDestroyView() {
+ presenter.onDetachView()
+ super.onDestroyView()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt
new file mode 100644
index 000000000..a1a48be17
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt
@@ -0,0 +1,142 @@
+package io.github.wulkanowy.ui.modules.studentinfo
+
+import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.db.entities.StudentInfo
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.data.repositories.StudentInfoRepository
+import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.base.ErrorHandler
+import io.github.wulkanowy.utils.AnalyticsHelper
+import io.github.wulkanowy.utils.afterLoading
+import io.github.wulkanowy.utils.flowWithResourceIn
+import io.github.wulkanowy.utils.getCurrentOrLast
+import kotlinx.coroutines.flow.onEach
+import timber.log.Timber
+import javax.inject.Inject
+
+class StudentInfoPresenter @Inject constructor(
+ errorHandler: ErrorHandler,
+ studentRepository: StudentRepository,
+ private val studentInfoRepository: StudentInfoRepository,
+ private val analytics: AnalyticsHelper
+) : BasePresenter(errorHandler, studentRepository) {
+
+ private lateinit var infoType: StudentInfoView.Type
+
+ private lateinit var studentWithSemesters: StudentWithSemesters
+
+ private lateinit var lastError: Throwable
+
+ fun onAttachView(
+ view: StudentInfoView,
+ type: StudentInfoView.Type,
+ studentWithSemesters: StudentWithSemesters
+ ) {
+ super.onAttachView(view)
+ infoType = type
+ this.studentWithSemesters = studentWithSemesters
+ view.initView()
+ Timber.i("Student info $infoType view was initialized")
+ errorHandler.showErrorMessage = ::showErrorViewOnError
+ loadData()
+ }
+
+ fun onSwipeRefresh() {
+ loadData(true)
+ }
+
+ fun onRetry() {
+ view?.run {
+ showErrorView(false)
+ showProgress(true)
+ }
+ loadData(true)
+ }
+
+ fun onDetailsClick() {
+ view?.showErrorDetailsDialog(lastError)
+ }
+
+ fun onItemSelected(position: Int) {
+ if (infoType != StudentInfoView.Type.FAMILY) return
+
+ if (position == 0) {
+ view?.openStudentInfoView(StudentInfoView.Type.FIRST_GUARDIAN, studentWithSemesters)
+ } else {
+ view?.openStudentInfoView(StudentInfoView.Type.SECOND_GUARDIAN, studentWithSemesters)
+ }
+ }
+
+ fun onItemLongClick(text: String) {
+ view?.copyToClipboard(text)
+ }
+
+ private fun loadData(forceRefresh: Boolean = false) {
+ flowWithResourceIn {
+ val semester = studentWithSemesters.semesters.getCurrentOrLast()
+ studentInfoRepository.getStudentInfo(
+ studentWithSemesters.student,
+ semester,
+ forceRefresh
+ )
+ }.onEach {
+ when (it.status) {
+ Status.LOADING -> Timber.i("Loading student info $infoType started")
+ Status.SUCCESS -> {
+ if (it.data != null) {
+ Timber.i("Loading student info $infoType result: Success")
+ showCorrectData(it.data)
+ view?.run {
+ showContent(true)
+ showEmpty(false)
+ showErrorView(false)
+ }
+ analytics.logEvent("load_item", "type" to "student_info")
+ } else {
+ Timber.i("Loading student info $infoType result: No school info found")
+ view?.run {
+ showContent(!isViewEmpty)
+ showEmpty(isViewEmpty)
+ showErrorView(false)
+ }
+ }
+ }
+ Status.ERROR -> {
+ Timber.i("Loading student info $infoType result: An exception occurred")
+ errorHandler.dispatch(it.error!!)
+ }
+ }
+ }.afterLoading {
+ view?.run {
+ hideRefresh()
+ showProgress(false)
+ enableSwipe(true)
+ }
+ }.launch()
+ }
+
+ private fun showCorrectData(studentInfo: StudentInfo) {
+ when (infoType) {
+ StudentInfoView.Type.PERSONAL -> view?.showPersonalTypeData(studentInfo)
+ StudentInfoView.Type.CONTACT -> view?.showContactTypeData(studentInfo)
+ StudentInfoView.Type.ADDRESS -> view?.showAddressTypeData(studentInfo)
+ StudentInfoView.Type.FAMILY -> view?.showFamilyTypeData(studentInfo)
+ StudentInfoView.Type.SECOND_GUARDIAN -> view?.showSecondGuardianTypeData(studentInfo)
+ StudentInfoView.Type.FIRST_GUARDIAN -> view?.showFirstGuardianTypeData(studentInfo)
+ }
+ }
+
+ private fun showErrorViewOnError(message: String, error: Throwable) {
+ view?.run {
+ if (isViewEmpty) {
+ lastError = error
+ setErrorDetails(message)
+ showErrorView(true)
+ showEmpty(false)
+ showContent(false)
+ showProgress(false)
+ } else showError(message, error)
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt
new file mode 100644
index 000000000..70c3eb5e0
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoView.kt
@@ -0,0 +1,48 @@
+package io.github.wulkanowy.ui.modules.studentinfo
+
+import io.github.wulkanowy.data.db.entities.StudentInfo
+import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.ui.base.BaseView
+
+interface StudentInfoView : BaseView {
+
+ enum class Type {
+ PERSONAL, ADDRESS, CONTACT, FAMILY, FIRST_GUARDIAN, SECOND_GUARDIAN
+ }
+
+ val isViewEmpty: Boolean
+
+ fun initView()
+
+ fun updateData(data: List>)
+
+ fun showPersonalTypeData(studentInfo: StudentInfo)
+
+ fun showContactTypeData(studentInfo: StudentInfo)
+
+ fun showAddressTypeData(studentInfo: StudentInfo)
+
+ fun showFamilyTypeData(studentInfo: StudentInfo)
+
+ fun showFirstGuardianTypeData(studentInfo: StudentInfo)
+
+ fun showSecondGuardianTypeData(studentInfo: StudentInfo)
+
+ fun openStudentInfoView(infoType: Type, studentWithSemesters: StudentWithSemesters)
+
+ fun showEmpty(show: Boolean)
+
+ fun showErrorView(show: Boolean)
+
+ fun setErrorDetails(message: String)
+
+ fun showProgress(show: Boolean)
+
+ fun enableSwipe(enable: Boolean)
+
+ fun showContent(show: Boolean)
+
+ fun hideRefresh()
+
+ fun copyToClipboard(text: String)
+}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt b/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt
index 1098a9a06..37a09e74b 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/AppInfo.kt
@@ -28,4 +28,9 @@ open class AppInfo @Inject constructor() {
@Suppress("DEPRECATION")
open val systemLanguage: String
get() = Resources.getSystem().configuration.locale.language
+
+ open val defaultColorsForAvatar = listOf(
+ 0xe57373, 0xf06292, 0xba68c8, 0x9575cd, 0x7986cb, 0x64b5f6, 0x4fc3f7, 0x4dd0e1, 0x4db6ac,
+ 0x81c784, 0xaed581, 0xff8a65, 0xd4e157, 0xffd54f, 0xffb74d, 0xa1887f, 0x90a4ae
+ )
}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt
index 2d074b222..049e1d42a 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt
@@ -2,6 +2,7 @@ package io.github.wulkanowy.utils
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.Status
+import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
@@ -71,24 +72,14 @@ inline fun networkBoundResource(
fun flowWithResource(block: suspend () -> T) = flow {
emit(Resource.loading())
- emit(try {
- Resource.success(block())
- } catch (e: Throwable) {
- Resource.error(e)
- })
-}
+ emit(Resource.success(block()))
+}.catch { emit(Resource.error(it)) }
+@OptIn(FlowPreview::class)
fun flowWithResourceIn(block: suspend () -> Flow>) = flow {
emit(Resource.loading())
-
- block()
- .catch { emit(Resource.error(it)) }
- .collect {
- if (it.status != Status.LOADING || (it.status == Status.LOADING && it.data != null)) { // LOADING without data is already emitted
- emit(it)
- }
- }
-}
+ emitAll(block().filter { it.status != Status.LOADING || (it.status == Status.LOADING && it.data != null) })
+}.catch { emit(Resource.error(it)) }
fun Flow>.afterLoading(callback: () -> Unit) = onEach {
if (it.status != Status.LOADING) callback()
@@ -96,4 +87,5 @@ fun Flow>.afterLoading(callback: () -> Unit) = onEach {
suspend fun Flow>.toFirstResult() = filter { it.status != Status.LOADING }.first()
-suspend fun Flow>.waitForResult() = takeWhile { it.status == Status.LOADING }.collect()
+suspend fun Flow>.waitForResult() =
+ takeWhile { it.status == Status.LOADING }.collect()
diff --git a/app/src/main/java/io/github/wulkanowy/utils/FragmentExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/FragmentExtension.kt
index 0b71c964b..a49360d7a 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/FragmentExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/FragmentExtension.kt
@@ -2,6 +2,8 @@ package io.github.wulkanowy.utils
import androidx.fragment.app.Fragment
import io.github.wulkanowy.ui.modules.about.AboutFragment
+import io.github.wulkanowy.ui.modules.account.AccountFragment
+import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
@@ -13,6 +15,7 @@ import io.github.wulkanowy.ui.modules.more.MoreFragment
import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
+import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
fun Fragment.toSection(): MainView.Section? {
@@ -29,6 +32,9 @@ fun Fragment.toSection(): MainView.Section? {
is SettingsFragment -> MainView.Section.SETTINGS
is AboutFragment -> MainView.Section.ABOUT
is SchoolAndTeachersFragment -> MainView.Section.SCHOOL
+ is AccountFragment -> MainView.Section.ACCOUNT
+ is AccountDetailsFragment -> MainView.Section.ACCOUNT
+ is StudentInfoFragment -> MainView.Section.STUDENT_INFO
else -> null
}
}
diff --git a/app/src/main/res/drawable/ic_account_details_family.xml b/app/src/main/res/drawable/ic_account_details_family.xml
new file mode 100644
index 000000000..363b4f58b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_account_details_family.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_about_homepage.xml b/app/src/main/res/drawable/ic_all_home.xml
similarity index 100%
rename from app/src/main/res/drawable/ic_about_homepage.xml
rename to app/src/main/res/drawable/ic_all_home.xml
diff --git a/app/src/main/res/drawable/ic_school_phone.xml b/app/src/main/res/drawable/ic_all_phone.xml
similarity index 100%
rename from app/src/main/res/drawable/ic_school_phone.xml
rename to app/src/main/res/drawable/ic_all_phone.xml
diff --git a/app/src/main/res/layout/dialog_account_edit_details.xml b/app/src/main/res/layout/dialog_account_edit_details.xml
new file mode 100644
index 000000000..0b13e0427
--- /dev/null
+++ b/app/src/main/res/layout/dialog_account_edit_details.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_account.xml b/app/src/main/res/layout/dialog_account_quick.xml
similarity index 62%
rename from app/src/main/res/layout/dialog_account.xml
rename to app/src/main/res/layout/dialog_account_quick.xml
index d56de3a2e..da31d31d3 100644
--- a/app/src/main/res/layout/dialog_account.xml
+++ b/app/src/main/res/layout/dialog_account_quick.xml
@@ -8,23 +8,24 @@
+ android:orientation="vertical"
+ tools:ignore="UselessParent">
-
-
-
-
+ android:text="@string/account_quick_manager" />
diff --git a/app/src/main/res/layout/fragment_account.xml b/app/src/main/res/layout/fragment_account.xml
new file mode 100644
index 000000000..a2cf73cf8
--- /dev/null
+++ b/app/src/main/res/layout/fragment_account.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_account_details.xml b/app/src/main/res/layout/fragment_account_details.xml
new file mode 100644
index 000000000..f0e1bd9b6
--- /dev/null
+++ b/app/src/main/res/layout/fragment_account_details.xml
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_school.xml b/app/src/main/res/layout/fragment_school.xml
index a7e9a213b..23126b086 100644
--- a/app/src/main/res/layout/fragment_school.xml
+++ b/app/src/main/res/layout/fragment_school.xml
@@ -140,7 +140,7 @@
android:id="@+id/schoolTelephoneButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- app:srcCompat="@drawable/ic_school_phone"
+ app:srcCompat="@drawable/ic_all_phone"
android:contentDescription="@string/school_telephone_button"
android:background="?attr/selectableItemBackgroundBorderless"
android:tint="?colorPrimary"
diff --git a/app/src/main/res/layout/fragment_student_info.xml b/app/src/main/res/layout/fragment_student_info.xml
new file mode 100644
index 000000000..371457272
--- /dev/null
+++ b/app/src/main/res/layout/fragment_student_info.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/header_account.xml b/app/src/main/res/layout/header_account.xml
index 6219c26db..862525522 100644
--- a/app/src/main/res/layout/header_account.xml
+++ b/app/src/main/res/layout/header_account.xml
@@ -4,15 +4,22 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:paddingHorizontal="24dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
+ android:paddingHorizontal="16dp"
+ android:paddingBottom="16dp"
tools:context=".ui.modules.account.AccountAdapter">
+
+
@@ -21,7 +28,6 @@
android:id="@+id/accountHeaderType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="2dp"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
tools:text="Konto ucznia" />
diff --git a/app/src/main/res/layout/item_account.xml b/app/src/main/res/layout/item_account.xml
index 9568b345a..95fdb0e8b 100644
--- a/app/src/main/res/layout/item_account.xml
+++ b/app/src/main/res/layout/item_account.xml
@@ -5,9 +5,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
+ android:clickable="true"
+ android:focusable="true"
android:orientation="horizontal"
- android:paddingVertical="8dp"
android:paddingHorizontal="16dp"
+ android:paddingVertical="8dp"
tools:context=".ui.modules.account.AccountAdapter">
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/action_menu_account_details.xml b/app/src/main/res/menu/action_menu_account_details.xml
new file mode 100644
index 000000000..9ddc1e550
--- /dev/null
+++ b/app/src/main/res/menu/action_menu_account_details.xml
@@ -0,0 +1,11 @@
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4364214cf..f48179890 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -17,7 +17,10 @@
New message
Notes and achievements
Homework
- Choose account
+ Accounts manager
+ Select account
+ Account details
+ Student info
@@ -344,12 +347,19 @@
Add account
Logout
- Do you want to log out of an active student?
+ Do you want to log out this student?
Student logout
Student account
Parent account
Mobile API mode
Hybrid mode
+ Edit data
+ Accounts manager
+ Select student
+ Family
+ Contact
+ Residence details
+ Personal information
@@ -382,6 +392,28 @@
See more on GitHub
+
+ No info about student
+ Name
+ Second name
+ Gender
+ Polish citizenship
+ Family name
+ Mother\'s and father\'s names
+ Phone
+ Cellphone
+ E-mail
+ Address of residence
+ Address of registration
+ Correspondence address
+ Surname and first name
+ Degree of kinship
+ Address
+ Phones
+ Male
+ Female
+
+
Share logs
Refresh
@@ -410,6 +442,8 @@
Next
Search
Search…
+ Yes
+ No
@@ -508,4 +542,5 @@
An unexpected error occurred
Feature disabled by your school
Feature not available. Login in a mode other than Mobile API
+
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/StudentTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/StudentTest.kt
index 22669323e..415f6e0af 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/StudentTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/StudentTest.kt
@@ -35,7 +35,9 @@ class StudentTest {
@Test
fun testRemoteAll() {
- coEvery { mockSdk.getStudentsFromScrapper(any(), any(), any(), any()) } returns listOf(getStudent("test"))
+ coEvery { mockSdk.getStudentsFromScrapper(any(), any(), any(), any()) } returns listOf(
+ getStudent("test")
+ )
val students = runBlocking { studentRepository.getStudentsScrapper("", "", "http://fakelog.cf", "") }
assertEquals(1, students.size)
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
index 4479965cd..fed0093e8 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
@@ -41,7 +41,29 @@ class GradeAverageProviderTest {
private lateinit var gradeAverageProvider: GradeAverageProvider
- private val student = Student("", "", "", "SCRAPPER", "", "", false, "", "", "", 101, 0, "", "", "", "", "", "", 1, true, LocalDateTime.now())
+ private val student = Student(
+ scrapperBaseUrl = "",
+ mobileBaseUrl = "",
+ loginType = "",
+ loginMode = "SCRAPPER",
+ certificateKey = "",
+ privateKey = "",
+ isParent = false,
+ email = "",
+ password = "",
+ symbol = "",
+ studentId = 101,
+ userLoginId = 0,
+ userName = "",
+ studentName = "",
+ schoolSymbol = "",
+ schoolShortName = "",
+ schoolName = "",
+ className = "",
+ classId = 1,
+ isCurrent = true,
+ registrationDate = LocalDateTime.now()
+ )
private val semesters = mutableListOf(
getSemesterEntity(10, 21, of(2019, 1, 31), of(2019, 6, 23)),
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt
index b475584b5..31e3e198b 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenterTest.kt
@@ -99,8 +99,32 @@ class LoginFormPresenterTest {
@Test
fun loginTest() {
- val studentTest = Student(email = "test@", password = "123", scrapperBaseUrl = "https://fakelog.cf/", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, classId = 1, isCurrent = false, symbol = "", registrationDate = now(), className = "", mobileBaseUrl = "", privateKey = "", certificateKey = "", loginMode = "", userLoginId = 0, schoolShortName = "", isParent = false, userName = "")
- coEvery { repository.getStudentsScrapper(any(), any(), any(), any()) } returns listOf(StudentWithSemesters(studentTest, emptyList()))
+ val studentTest = Student(
+ email = "test@",
+ password = "123",
+ scrapperBaseUrl = "https://fakelog.cf/",
+ loginType = "AUTO",
+ studentName = "",
+ schoolSymbol = "",
+ schoolName = "",
+ studentId = 0,
+ classId = 1,
+ isCurrent = false,
+ symbol = "",
+ registrationDate = now(),
+ className = "",
+ mobileBaseUrl = "",
+ privateKey = "",
+ certificateKey = "",
+ loginMode = "",
+ userLoginId = 0,
+ schoolShortName = "",
+ isParent = false,
+ userName = ""
+ )
+ coEvery { repository.getStudentsScrapper(any(), any(), any(), any()) } returns listOf(
+ StudentWithSemesters(studentTest, emptyList())
+ )
every { loginFormView.formUsernameValue } returns "@"
every { loginFormView.formPassValue } returns "123456"
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt
index 4b9776825..9e34235e7 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenterTest.kt
@@ -38,7 +38,31 @@ class LoginStudentSelectPresenterTest {
private lateinit var presenter: LoginStudentSelectPresenter
- private val testStudent by lazy { Student(email = "test", password = "test123", scrapperBaseUrl = "https://fakelog.cf", loginType = "AUTO", symbol = "", isCurrent = false, studentId = 0, schoolName = "", schoolSymbol = "", classId = 1, studentName = "", registrationDate = now(), className = "", loginMode = "", certificateKey = "", privateKey = "", mobileBaseUrl = "", schoolShortName = "", userLoginId = 1, isParent = false, userName = "") }
+ private val testStudent by lazy {
+ Student(
+ email = "test",
+ password = "test123",
+ scrapperBaseUrl = "https://fakelog.cf",
+ loginType = "AUTO",
+ symbol = "",
+ isCurrent = false,
+ studentId = 0,
+ schoolName = "",
+ schoolSymbol = "",
+ classId = 1,
+ studentName = "",
+ registrationDate = now(),
+ className = "",
+ loginMode = "",
+ certificateKey = "",
+ privateKey = "",
+ mobileBaseUrl = "",
+ schoolShortName = "",
+ userLoginId = 1,
+ isParent = false,
+ userName = ""
+ )
+ }
private val testException by lazy { RuntimeException("Problem") }
@@ -64,8 +88,24 @@ class LoginStudentSelectPresenterTest {
@Test
fun onSelectedStudentTest() {
- coEvery { studentRepository.saveStudents(listOf(StudentWithSemesters(testStudent, emptyList()))) } returns listOf(1L)
- coEvery { studentRepository.switchStudent(StudentWithSemesters(testStudent, emptyList())) } just Runs
+ coEvery {
+ studentRepository.saveStudents(
+ listOf(
+ StudentWithSemesters(
+ testStudent,
+ emptyList()
+ )
+ )
+ )
+ } returns listOf(1L)
+ coEvery {
+ studentRepository.switchStudent(
+ StudentWithSemesters(
+ testStudent,
+ emptyList()
+ )
+ )
+ } just Runs
every { loginStudentSelectView.openMainView() } just Runs
presenter.onItemSelected(StudentWithSemesters(testStudent, emptyList()), false)
presenter.onSignIn()
@@ -77,7 +117,16 @@ class LoginStudentSelectPresenterTest {
@Test
fun onSelectedStudentErrorTest() {
- coEvery { studentRepository.saveStudents(listOf(StudentWithSemesters(testStudent, emptyList()))) } throws testException
+ coEvery {
+ studentRepository.saveStudents(
+ listOf(
+ StudentWithSemesters(
+ testStudent,
+ emptyList()
+ )
+ )
+ )
+ } throws testException
coEvery { studentRepository.logoutStudent(testStudent) } just Runs
presenter.onItemSelected(StudentWithSemesters(testStudent, emptyList()), false)
presenter.onSignIn()
diff --git a/gradle.properties b/gradle.properties
index 5741ecfc4..998e31957 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -7,10 +7,13 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
android.enableJetifier=true
android.useAndroidX=true
-org.gradle.jvmargs=-Xmx1536m
+kotlin.code.style=official
kapt.incremental.apt=true
+kapt.use.worker.api=true
+kapt.include.compile.classpath=false
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit