From 3894c9d48edc0ea7bdb92618e1ffbd41e534b833 Mon Sep 17 00:00:00 2001 From: stha <48914870+sthakrk@users.noreply.github.com> Date: Mon, 9 Dec 2019 17:34:58 +0100 Subject: [PATCH 001/236] Fixed letter capitalization in the About tab (pl) (#612) --- app/src/main/res/values-pl/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 2eeee5fcb..9026fa0c1 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -261,8 +261,8 @@ Wyślij zgłoszenie o błędzie poprzez e-maila FAQ Zobacz Najczęściej Zadawane Pytania - Serwer discord - Dołącz do społeczności wulkanowego + Serwer Discord + Dołącz do społeczności Wulkanowego Polityka prywatności Zasady zbierania danych osobowych Strona domowa From 91f63da6d08788c735f4318cd39f109993481dc8 Mon Sep 17 00:00:00 2001 From: stha <48914870+sthakrk@users.noreply.github.com> Date: Mon, 9 Dec 2019 17:35:18 +0100 Subject: [PATCH 002/236] Fixed capitalization in the about tab (en) (#613) --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 13bdfc790..e056e35ac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -243,7 +243,7 @@ FAQ Read Frequently Asked Questions Discord server - Join the wulkanowy community + Join the Wulkanowy community Privacy policy Rules for collecting personal data Homepage From 4639a075b0998a2de501dc2c5bcc27258a7c0d09 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2019 09:06:52 +0000 Subject: [PATCH 003/236] Bump rxjava from 2.2.15 to 2.2.16 (#615) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 177f15b7b..fd3646e13 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -167,7 +167,7 @@ dependencies { implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6" implementation "io.reactivex.rxjava2:rxandroid:2.1.1" - implementation "io.reactivex.rxjava2:rxjava:2.2.15" + implementation "io.reactivex.rxjava2:rxjava:2.2.16" implementation "com.google.code.gson:gson:2.8.6" implementation "com.jakewharton.threetenabp:threetenabp:1.2.1" From 93bce685bd43f03e6cc9eef98444a4448f0c9d19 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2019 09:07:39 +0000 Subject: [PATCH 004/236] Bump dagger from 2.25.2 to 2.25.3 (#614) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index fd3646e13..c29c23fea 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -112,7 +112,7 @@ play { ext { work_manager = "2.3.0-beta01" room = "2.2.2" - dagger = "2.25.2" + dagger = "2.25.3" chucker = "2.0.4" mockk = "1.9.2" } From 90bdc9d157484ee7d2c4bd2d5e0cc146fbce8584 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2019 09:13:34 +0000 Subject: [PATCH 005/236] Bump material from 1.1.0-beta02 to 1.1.0-rc01 (#616) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index c29c23fea..126a7bb2d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -139,7 +139,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03" implementation "androidx.constraintlayout:constraintlayout:1.1.3" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" - implementation "com.google.android.material:material:1.1.0-beta02" + implementation "com.google.android.material:material:1.1.0-rc01" implementation "com.github.wulkanowy:material-chips-input:2.0.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "me.zhanghai.android.materialprogressbar:library:1.6.1" From 57abd43214ec425a834250dcbc19df98611ef631 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2019 09:24:51 +0000 Subject: [PATCH 006/236] Bump fragment-ktx from 1.2.0-rc03 to 1.2.0-rc04 (#617) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 126a7bb2d..bb032eb85 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -129,7 +129,7 @@ dependencies { implementation "androidx.activity:activity-ktx:1.1.0-rc03" implementation "androidx.appcompat:appcompat:1.1.0" implementation "androidx.appcompat:appcompat-resources:1.1.0" - implementation "androidx.fragment:fragment-ktx:1.2.0-rc03" + implementation "androidx.fragment:fragment-ktx:1.2.0-rc04" implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.multidex:multidex:2.0.1" From cf286f3c23d3fd0a06e045878a4446406a047b09 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2019 09:27:14 +0000 Subject: [PATCH 007/236] Bump mockito-android from 3.2.0 to 3.2.4 (#619) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index bb032eb85..90e69cb30 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -196,7 +196,7 @@ dependencies { androidTestImplementation "io.mockk:mockk-android:$mockk" androidTestImplementation "androidx.room:room-testing:$room" androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" - androidTestImplementation "org.mockito:mockito-android:3.2.0" + androidTestImplementation "org.mockito:mockito-android:3.2.4" } apply plugin: 'com.google.gms.google-services' From bc4335946733a9e4992f6a01f8714df355524619 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2019 09:44:18 +0000 Subject: [PATCH 008/236] Bump mockito-inline from 3.2.0 to 3.2.4 (#621) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 90e69cb30..5c15fc80f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -188,7 +188,7 @@ dependencies { testImplementation "junit:junit:4.12" testImplementation "io.mockk:mockk:$mockk" testImplementation "org.threeten:threetenbp:1.4.0" - testImplementation "org.mockito:mockito-inline:3.2.0" + testImplementation "org.mockito:mockito-inline:3.2.4" androidTestImplementation "androidx.test:core:1.2.0" androidTestImplementation "androidx.test:runner:1.2.0" From d70c4fa9fe83e5f138c1584decb06297fac15f90 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2019 09:46:28 +0000 Subject: [PATCH 009/236] Bump work_manager from 2.3.0-beta01 to 2.3.0-beta02 (#620) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 5c15fc80f..4dc070ffb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -110,7 +110,7 @@ play { } ext { - work_manager = "2.3.0-beta01" + work_manager = "2.3.0-beta02" room = "2.2.2" dagger = "2.25.3" chucker = "2.0.4" From 826ea32fc08cbbb1048dc197e7ff429299153479 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2019 10:18:00 +0000 Subject: [PATCH 010/236] Bump room from 2.2.2 to 2.2.3 (#618) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 4dc070ffb..b5df91c6b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -111,7 +111,7 @@ play { ext { work_manager = "2.3.0-beta02" - room = "2.2.2" + room = "2.2.3" dagger = "2.25.3" chucker = "2.0.4" mockk = "1.9.2" From 304c49d61ed23e369d2a9d23f675bd4a87cc3585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 22 Dec 2019 00:14:46 +0100 Subject: [PATCH 011/236] Migration to Wulkanowy SDK (#336) --- app/build.gradle | 2 +- .../19.json | 1628 +++++++++++++++++ .../db/migrations/AbstractMigrationTest.kt | 6 +- .../repositories/grade/GradeRepositoryTest.kt | 29 +- .../grade/TestGradeEntityCreator.kt | 25 +- .../recipient/RecipientLocalTest.kt | 2 +- .../repositories/student/StudentLocalTest.kt | 2 +- .../timetable/TestTimetableEntityCreator.kt | 20 +- .../timetable/TimetableLocalTest.kt | 6 +- .../timetable/TimetableRepositoryTest.kt | 50 +- .../java/io/github/wulkanowy/WulkanowyApp.kt | 13 - .../io/github/wulkanowy/data/ApiHelper.kt | 35 - .../github/wulkanowy/data/RepositoryModule.kt | 17 +- .../io/github/wulkanowy/data/SdkHelper.kt | 30 + .../github/wulkanowy/data/db/AppDatabase.kt | 12 +- .../wulkanowy/data/db/SharedPrefProvider.kt | 6 + .../wulkanowy/data/db/entities/Grade.kt | 2 +- .../wulkanowy/data/db/entities/Message.kt | 4 +- .../wulkanowy/data/db/entities/Student.kt | 22 +- .../data/db/migrations/Migration19.kt | 119 ++ .../attendance/AttendanceRemote.kt | 11 +- .../AttendanceSummaryRemote.kt | 8 +- .../CompletedLessonsRemote.kt | 10 +- .../data/repositories/exam/ExamRemote.kt | 13 +- .../data/repositories/grade/GradeRemote.kt | 16 +- .../gradessummary/GradeSummaryRemote.kt | 9 +- .../gradestatistics/GradeStatisticsRemote.kt | 37 +- .../repositories/homework/HomeworkRemote.kt | 12 +- .../luckynumber/LuckyNumberRemote.kt | 8 +- .../repositories/message/MessageRemote.kt | 30 +- .../repositories/message/MessageRepository.kt | 19 +- .../mobiledevice/MobileDeviceRemote.kt | 16 +- .../data/repositories/note/NoteRemote.kt | 10 +- .../repositories/recipient/RecipientRemote.kt | 14 +- .../recipient/RecipientRepository.kt | 8 +- .../reportingunit/ReportingUnitRemote.kt | 8 +- .../reportingunit/ReportingUnitRepository.kt | 8 +- .../data/repositories/school/SchoolRemote.kt | 7 +- .../repositories/semester/SemesterRemote.kt | 31 +- .../semester/SemesterRepository.kt | 6 +- .../data/repositories/student/StudentLocal.kt | 14 +- .../repositories/student/StudentRemote.kt | 59 +- .../repositories/student/StudentRepository.kt | 30 +- .../repositories/subject/SubjectRemote.kt | 9 +- .../repositories/teacher/TeacherRemote.kt | 7 +- .../repositories/timetable/TimetableRemote.kt | 15 +- .../wulkanowy/services/sync/SyncWorker.kt | 3 +- .../services/sync/works/MessageWork.kt | 2 +- .../github/wulkanowy/ui/base/BaseActivity.kt | 4 +- .../github/wulkanowy/ui/base/ErrorHandler.kt | 10 +- .../ui/modules/grade/GradeAverageProvider.kt | 5 +- .../ui/modules/grade/GradeFragment.kt | 1 - .../grade/details/GradeDetailsFragment.kt | 1 - .../ui/modules/login/LoginActivity.kt | 8 +- .../ui/modules/login/LoginErrorHandler.kt | 2 +- .../wulkanowy/ui/modules/login/LoginModule.kt | 5 + .../ui/modules/login/LoginPresenter.kt | 6 +- .../login/advanced/LoginAdvancedFragment.kt | 227 +++ .../login/advanced/LoginAdvancedPresenter.kt | 185 ++ .../login/advanced/LoginAdvancedView.kt | 65 + .../modules/login/form/LoginFormFragment.kt | 14 +- .../modules/login/form/LoginFormPresenter.kt | 22 +- .../ui/modules/login/form/LoginFormView.kt | 4 +- .../LoginStudentSelectPresenter.kt | 4 +- .../login/symbol/LoginSymbolPresenter.kt | 6 +- .../ui/modules/message/MessageItem.kt | 3 +- .../preview/MessagePreviewPresenter.kt | 7 +- .../message/tab/MessageTabPresenter.kt | 9 +- .../wulkanowy/ui/modules/more/MoreFragment.kt | 1 - .../completed/CompletedLessonsErrorHandler.kt | 2 +- .../TimetableWidgetProvider.kt | 1 - .../github/wulkanowy/utils/GradeExtension.kt | 4 +- .../wulkanowy/utils/security/Scrambler.kt | 3 +- .../res/layout/fragment_login_advanced.xml | 269 +++ .../main/res/layout/fragment_login_form.xml | 69 +- .../main/res/layout/fragment_login_symbol.xml | 2 +- app/src/main/res/values-pl/strings.xml | 8 + app/src/main/res/values/strings.xml | 8 + .../attendance/AttendanceRemoteTest.kt | 58 +- .../CompletedLessonsRemoteTest.kt | 45 +- .../data/repositories/exam/ExamRemoteTest.kt | 61 +- .../GradeStatisticsRemoteTest.kt | 48 +- .../luckynumber/LuckyNumberRemoteTest.kt | 11 +- .../semester/SemesterRepositoryTest.kt | 10 +- .../repositories/student/StudentRemoteTest.kt | 36 +- .../timetable/TimetableRemoteTest.kt | 57 +- .../modules/grade/GradeAverageProviderTest.kt | 76 +- .../login/form/LoginFormPresenterTest.kt | 15 +- .../LoginStudentSelectPresenterTest.kt | 2 +- .../wulkanowy/utils/GradeExtensionTest.kt | 33 +- 90 files changed, 3287 insertions(+), 570 deletions(-) create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/19.json delete mode 100644 app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/SdkHelper.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration19.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt create mode 100644 app/src/main/res/layout/fragment_login_advanced.xml diff --git a/app/build.gradle b/app/build.gradle index b5df91c6b..d14972697 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -122,7 +122,7 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:api:0.13.0" + implementation "io.github.wulkanowy:sdk:4e21f7d" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0-rc01" diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/19.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/19.json new file mode 100644 index 000000000..1e4593bb3 --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/19.json @@ -0,0 +1,1628 @@ +{ + "formatVersion": 1, + "database": { + "version": 19, + "identityHash": "294f40cebf8314f9776208827240e65f", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `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": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '294f40cebf8314f9776208827240e65f')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt index 74cac078c..611161e58 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt @@ -1,11 +1,13 @@ package io.github.wulkanowy.data.db.migrations +import androidx.preference.PreferenceManager import androidx.room.Room import androidx.room.testing.MigrationTestHelper import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory import androidx.test.core.app.ApplicationProvider import androidx.test.platform.app.InstrumentationRegistry import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.SharedPrefProvider import org.junit.Rule abstract class AbstractMigrationTest { @@ -22,7 +24,9 @@ abstract class AbstractMigrationTest { fun getMigratedRoomDatabase(): AppDatabase { val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java, dbName) - .addMigrations(*AppDatabase.getMigrations()) + .addMigrations(*AppDatabase.getMigrations(SharedPrefProvider(PreferenceManager + .getDefaultSharedPreferences(ApplicationProvider.getApplicationContext()))) + ) .build() // close the database and release any stream resources when the test finishes helper.closeWhenFinished(database) diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt index a0acb5a76..711e29118 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/GradeRepositoryTest.kt @@ -6,15 +6,18 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy +import io.github.wulkanowy.sdk.Sdk import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.SpyK +import io.mockk.just +import io.mockk.runs import io.reactivex.Single import org.junit.After import org.junit.Before @@ -25,14 +28,13 @@ import org.threeten.bp.LocalDateTime import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue -import io.github.wulkanowy.api.grades.Grade as GradeApi @SdkSuppress(minSdkVersion = P) @RunWith(AndroidJUnit4::class) class GradeRepositoryTest { - @SpyK - private var mockApi = Api() + @MockK + private lateinit var mockSdk: Sdk private val settings = InternetObservingSettings.builder() .strategy(TestInternetObservingStrategy()) @@ -55,13 +57,14 @@ class GradeRepositoryTest { MockKAnnotations.init(this) testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() gradeLocal = GradeLocal(testDb.gradeDao) - gradeRemote = GradeRemote(mockApi) + gradeRemote = GradeRemote(mockSdk) - every { mockApi.diaryId } returns 1 every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0) every { semesterMock.studentId } returns 1 - every { semesterMock.semesterId } returns 1 every { semesterMock.diaryId } returns 1 + every { semesterMock.schoolYear } returns 2019 + every { semesterMock.semesterId } returns 1 + every { mockSdk.switchDiary(any(), any()) } returns mockSdk } @After @@ -71,7 +74,7 @@ class GradeRepositoryTest { @Test fun markOlderThanRegisterDateAsRead() { - every { mockApi.getGrades(1) } returns Single.just(listOf( + every { mockSdk.getGrades(1) } returns Single.just(listOf( createGradeApi(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"), createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"), createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"), @@ -95,7 +98,7 @@ class GradeRepositoryTest { createGradeLocal(3, 5.0, of(2019, 2, 27), "Trzecia") )) - every { mockApi.getGrades(1) } returns Single.just(listOf( + every { mockSdk.getGrades(1) } returns Single.just(listOf( createGradeApi(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"), createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"), createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"), @@ -119,7 +122,7 @@ class GradeRepositoryTest { createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") )) - every { mockApi.getGrades(1) } returns Single.just(listOf( + every { mockSdk.getGrades(1) } returns Single.just(listOf( createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") )) @@ -137,7 +140,7 @@ class GradeRepositoryTest { createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") )) - every { mockApi.getGrades(1) } returns Single.just(listOf( + every { mockSdk.getGrades(1) } returns Single.just(listOf( createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") @@ -153,7 +156,7 @@ class GradeRepositoryTest { fun emptyLocal() { gradeLocal.saveGrades(listOf()) - every { mockApi.getGrades(1) } returns Single.just(listOf( + every { mockSdk.getGrades(1) } returns Single.just(listOf( createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") @@ -172,7 +175,7 @@ class GradeRepositoryTest { createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") )) - every { mockApi.getGrades(1) } returns Single.just(listOf()) + every { mockSdk.getGrades(1) } returns Single.just(listOf()) val grades = GradeRepository(settings, gradeLocal, gradeRemote) .getGrades(studentMock, semesterMock, true).blockingGet() diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt index e0fd05a82..9146934bf 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/grade/TestGradeEntityCreator.kt @@ -1,8 +1,7 @@ package io.github.wulkanowy.data.repositories.grade -import io.github.wulkanowy.api.toDate import org.threeten.bp.LocalDate -import io.github.wulkanowy.api.grades.Grade as GradeRemote +import io.github.wulkanowy.sdk.pojo.Grade as GradeRemote import io.github.wulkanowy.data.db.entities.Grade as GradeLocal fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String, semesterId: Int = 1): GradeLocal { @@ -18,17 +17,25 @@ fun createGradeLocal(value: Int, weight: Double, date: LocalDate, desc: String, description = desc, entry = "", gradeSymbol = "", - value = value, + value = value.toDouble(), weight = "", weightValue = weight ) } fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String): GradeRemote { - return GradeRemote().apply { - this.value = value - this.weightValue = weight - this.date = date.toDate() - this.description = desc - } + return GradeRemote( + subject = "", + color = "", + comment = "", + date = date, + description = desc, + entry = "", + modifier = .0, + symbol = "", + teacher = "", + value = value.toDouble(), + weight = weight.toString(), + weightValue = weight + ) } diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt index 6edaccdb4..a394927bd 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/recipient/RecipientLocalTest.kt @@ -42,7 +42,7 @@ class RecipientLocalTest { )) val recipients = recipientLocal.getRecipients( - Student("fakelog.cf", "AUTO", "", "", "", 1, "", "", "", "", 1, true, LocalDateTime.now()), + Student("fakelog.cf", "AUTO", "", "", "", "", false, "", "", "", 1, 0, "", "", "", "", 1, true, LocalDateTime.now()), 2, ReportingUnit(1, 4, "", 0, "", emptyList()) ).blockingGet() diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt index ee4c652f4..6ae404b2b 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/student/StudentLocalTest.kt @@ -39,7 +39,7 @@ class StudentLocalTest { @Test fun saveAndReadTest() { - studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", endpoint = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = ""))) + studentLocal.saveStudents(listOf(Student(email = "test", password = "test123", schoolSymbol = "23", scrapperBaseUrl = "fakelog.cf", loginType = "AUTO", isCurrent = true, studentName = "", schoolName = "", studentId = 0, classId = 1, symbol = "", registrationDate = now(), className = "", loginMode = "API", certificateKey = "", privateKey = "", mobileBaseUrl = "", userLoginId = 0, isParent = false))) .blockingGet() val student = studentLocal.getCurrentStudent(true).blockingGet() diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt index c38ffd99f..e2ce2255c 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt @@ -1,13 +1,11 @@ package io.github.wulkanowy.data.repositories.timetable -import io.github.wulkanowy.api.toDate -import io.github.wulkanowy.utils.toDate import org.threeten.bp.LocalDateTime import org.threeten.bp.LocalDateTime.now -import io.github.wulkanowy.api.timetable.Timetable as TimetableRemote +import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal -fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal { +fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal { return TimetableLocal( studentId = 1, diaryId = 2, @@ -28,18 +26,22 @@ fun createTimetableLocal(number: Int, start: LocalDateTime, room: String = "", s ) } -fun createTimetableRemote(number: Int, start: LocalDateTime, room: String, subject: String = "", teacher: String = "", changes: Boolean = false): TimetableRemote { +fun createTimetableRemote(start: LocalDateTime, number: Int = 1, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableRemote { return TimetableRemote( number = number, - start = start.toDate(), - end = start.plusMinutes(45).toDate(), - date = start.toLocalDate().toDate(), + start = start, + end = start.plusMinutes(45), + date = start.toLocalDate(), subject = subject, group = "", room = room, teacher = teacher, info = "", changes = changes, - canceled = false + canceled = false, + roomOld = "", + subjectOld = "", + teacherOld = "", + studentPlan = true ) } diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt index fe25e4e96..6406d7435 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableLocalTest.kt @@ -35,9 +35,9 @@ class TimetableLocalTest { @Test fun saveAndReadTest() { timetableDb.saveTimetable(listOf( - createTimetableLocal(1, of(2018, 9, 10, 0, 0, 0)), - createTimetableLocal(1, of(2018, 9, 14, 0, 0, 0)), - createTimetableLocal(1, of(2018, 9, 17, 0, 0, 0)) + createTimetableLocal(of(2018, 9, 10, 0, 0, 0), 1), + createTimetableLocal(of(2018, 9, 14, 0, 0, 0), 1), + createTimetableLocal(of(2018, 9, 17, 0, 0, 0), 1) )) val exams = timetableDb.getTimetable( diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt index 02ab605ed..073f822a2 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt @@ -6,14 +6,17 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.api.Api +import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy +import io.github.wulkanowy.sdk.Sdk import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.SpyK +import io.mockk.just +import io.mockk.runs import io.reactivex.Single import org.junit.After import org.junit.Before @@ -27,8 +30,8 @@ import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) class TimetableRepositoryTest { - @SpyK - private var mockApi = Api() + @MockK + private lateinit var mockSdk: Sdk private val settings = InternetObservingSettings.builder() .strategy(TestInternetObservingStrategy()) @@ -48,10 +51,13 @@ class TimetableRepositoryTest { MockKAnnotations.init(this) testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() timetableLocal = TimetableLocal(testDb.timetableDao) - timetableRemote = TimetableRemote(mockApi) + timetableRemote = TimetableRemote(mockSdk) every { semesterMock.studentId } returns 1 every { semesterMock.diaryId } returns 2 + every { semesterMock.schoolYear } returns 2019 + every { semesterMock.semesterId } returns 1 + every { mockSdk.switchDiary(any(), any()) } returns mockSdk } @After @@ -62,17 +68,17 @@ class TimetableRepositoryTest { @Test fun copyRoomToCompletedFromPrevious() { timetableLocal.saveTimetable(listOf( - createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda"), - createTimetableLocal(2, of(2019, 3, 5, 8, 50), "321", "Religia"), - createTimetableLocal(3, of(2019, 3, 5, 9, 40), "213", "W-F"), - createTimetableLocal(4, of(2019, 3, 5, 10, 30), "213", "W-F", "Jan Kowalski") + createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "123", "Przyroda"), + createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "321", "Religia"), + createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "213", "W-F"), + createTimetableLocal(of(2019, 3, 5, 10, 30),3, "213", "W-F", "Jan Kowalski") )) - every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf( - createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda"), - createTimetableRemote(2, of(2019, 3, 5, 8, 50), "", "Religia"), - createTimetableRemote(3, of(2019, 3, 5, 9, 40), "", "W-F"), - createTimetableRemote(4, of(2019, 3, 5, 10, 30), "", "W-F") + every { mockSdk.getTimetable(any(), any()) } returns Single.just(listOf( + createTimetableRemote(of(2019, 3, 5, 8, 0), 1, "", "Przyroda"), + createTimetableRemote(of(2019, 3, 5, 8, 50), 2, "", "Religia"), + createTimetableRemote(of(2019, 3, 5, 9, 40), 3, "", "W-F"), + createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F") )) val lessons = TimetableRepository(settings, timetableLocal, timetableRemote) @@ -88,17 +94,17 @@ class TimetableRepositoryTest { @Test fun copyTeacherToCompletedFromPrevious() { timetableLocal.saveTimetable(listOf( - createTimetableLocal(1, of(2019, 3, 5, 8, 0), "123", "Przyroda", "Jan Garnkiewicz", false), - createTimetableLocal(2, of(2019, 3, 5, 8, 50), "321", "Religia", "Paweł Jumper", false), - createTimetableLocal(3, of(2019, 3, 5, 9, 40), "213", "W-F", "", true), - createTimetableLocal(4, of(2019, 3, 5, 10, 30), "213", "W-F", "", false) + createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "123", "Przyroda", "Jan Garnkiewicz", false), + createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "321", "Religia", "Paweł Jumper", false), + createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "213", "W-F", "", true), + createTimetableLocal(of(2019, 3, 5, 10, 30), 4, "213", "W-F", "", false) )) - every { mockApi.getTimetable(any(), any()) } returns Single.just(listOf( - createTimetableRemote(1, of(2019, 3, 5, 8, 0), "", "Przyroda", "", true), // should override local - createTimetableRemote(2, of(2019, 3, 5, 8, 50), "", "Religia", "", false), - createTimetableRemote(3, of(2019, 3, 5, 9, 40), "", "W-F", "Jan Garnkiewicz", false), - createTimetableRemote(4, of(2019, 3, 5, 10, 30), "", "W-F", "Paweł Jumper", false) + every { mockSdk.getTimetable(any(), any()) } returns Single.just(listOf( + createTimetableRemote(of(2019, 3, 5, 8, 0), 1, "", "Przyroda", "", true), // should override local + createTimetableRemote(of(2019, 3, 5, 8, 50), 2, "", "Religia", "", false), + createTimetableRemote(of(2019, 3, 5, 9, 40), 3, "", "W-F", "Jan Garnkiewicz", false), + createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F", "Paweł Jumper", false) )) val lessons = TimetableRepository(settings, timetableLocal, timetableRemote) diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt index eee70891e..90b3581c8 100644 --- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt +++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt @@ -11,8 +11,6 @@ import dagger.android.AndroidInjector import dagger.android.support.DaggerApplication import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.utils.Log -import io.github.wulkanowy.data.db.SharedPrefProvider -import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY import io.github.wulkanowy.di.DaggerAppComponent import io.github.wulkanowy.services.sync.SyncWorkerFactory import io.github.wulkanowy.ui.base.ThemeManager @@ -35,9 +33,6 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider { @Inject lateinit var themeManager: ThemeManager - @Inject - lateinit var sharedPrefProvider: SharedPrefProvider - @Inject lateinit var appInfo: AppInfo @@ -52,7 +47,6 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider { RxJavaPlugins.setErrorHandler(::onError) Lingver.init(this) themeManager.applyDefaultTheme() - migrateSharedPreferences() initLogging() initCrashlytics(this, appInfo) @@ -68,13 +62,6 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider { registerActivityLifecycleCallbacks(ActivityLifecycleLogger()) } - private fun migrateSharedPreferences() { - if (sharedPrefProvider.getLong(APP_VERSION_CODE_KEY, -1) < 48) { // #596 - sharedPrefProvider.delete(getString(R.string.pref_key_grade_modifier_plus)) - sharedPrefProvider.delete(getString(R.string.pref_key_grade_modifier_minus)) - } - } - private fun onError(error: Throwable) { //RxJava's too deep stack traces may cause SOE on older android devices val cause = error.cause diff --git a/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt b/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt deleted file mode 100644 index b6eee316b..000000000 --- a/app/src/main/java/io/github/wulkanowy/data/ApiHelper.kt +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.wulkanowy.data - -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.data.db.entities.Student -import java.net.URL -import javax.inject.Inject - -class ApiHelper @Inject constructor(private val api: Api) { - - fun initApi(student: Student) { - api.apply { - email = student.email - password = student.password - symbol = student.symbol - schoolSymbol = student.schoolSymbol - studentId = student.studentId - classId = student.classId - host = URL(student.endpoint).run { host + ":$port".removeSuffix(":-1") } - ssl = student.endpoint.startsWith("https") - loginType = Api.LoginType.valueOf(student.loginType) - useNewStudent = true - } - } - - fun initApi(email: String, password: String, symbol: String, endpoint: String) { - api.apply { - this.email = email - this.password = password - this.symbol = symbol - host = URL(endpoint).run { host + ":$port".removeSuffix(":-1") } - ssl = endpoint.startsWith("https") - useNewStudent = true - } - } -} 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 9c89d507c..1bc4eb616 100644 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt @@ -11,12 +11,10 @@ import com.readystatesoftware.chuck.api.ChuckInterceptor import com.readystatesoftware.chuck.api.RetentionManager import dagger.Module import dagger.Provides -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.AppDatabase +import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository -import okhttp3.logging.HttpLoggingInterceptor -import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC -import okhttp3.logging.HttpLoggingInterceptor.Level.NONE +import io.github.wulkanowy.sdk.Sdk import timber.log.Timber import javax.inject.Singleton @@ -33,15 +31,14 @@ internal class RepositoryModule { @Singleton @Provides - fun provideApi(chuckCollector: ChuckCollector, context: Context): Api { - return Api().apply { - logLevel = NONE + fun provideSdk(chuckCollector: ChuckCollector, context: Context): Sdk { + return Sdk().apply { androidVersion = android.os.Build.VERSION.RELEASE buildTag = android.os.Build.MODEL - setInterceptor(HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { Timber.d(it) }).setLevel(BASIC)) + setSimpleHttpLogger { Timber.d(it) } // for debug only - setInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true, 0) + addInterceptor(ChuckInterceptor(context, chuckCollector).maxContentLength(250000L), true) } } @@ -55,7 +52,7 @@ internal class RepositoryModule { @Singleton @Provides - fun provideDatabase(context: Context) = AppDatabase.newInstance(context) + fun provideDatabase(context: Context, sharedPrefProvider: SharedPrefProvider) = AppDatabase.newInstance(context, sharedPrefProvider) @Singleton @Provides diff --git a/app/src/main/java/io/github/wulkanowy/data/SdkHelper.kt b/app/src/main/java/io/github/wulkanowy/data/SdkHelper.kt new file mode 100644 index 000000000..901712594 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/SdkHelper.kt @@ -0,0 +1,30 @@ +package io.github.wulkanowy.data + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.Sdk +import javax.inject.Inject + +class SdkHelper @Inject constructor(private val sdk: Sdk) { + + fun init(student: Student) { + sdk.apply { + email = student.email + password = student.password + symbol = student.symbol + schoolSymbol = student.schoolSymbol + studentId = student.studentId + classId = student.classId + + if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) { + scrapperBaseUrl = student.scrapperBaseUrl + loginType = Sdk.ScrapperLoginType.valueOf(student.loginType) + } + loginId = student.userLoginId + + mode = Sdk.Mode.valueOf(student.loginMode) + mobileBaseUrl = student.mobileBaseUrl + certKey = student.certificateKey + privateKey = student.privateKey + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index b1896c755..9ee1b263d 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 @@ -58,6 +58,7 @@ import io.github.wulkanowy.data.db.migrations.Migration15 import io.github.wulkanowy.data.db.migrations.Migration16 import io.github.wulkanowy.data.db.migrations.Migration17 import io.github.wulkanowy.data.db.migrations.Migration18 +import io.github.wulkanowy.data.db.migrations.Migration19 import io.github.wulkanowy.data.db.migrations.Migration2 import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration4 @@ -100,9 +101,9 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 18 + const val VERSION_SCHEMA = 19 - fun getMigrations(): Array { + fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array { return arrayOf( Migration2(), Migration3(), @@ -120,16 +121,17 @@ abstract class AppDatabase : RoomDatabase() { Migration15(), Migration16(), Migration17(), - Migration18() + Migration18(), + Migration19(sharedPrefProvider) ) } - fun newInstance(context: Context): AppDatabase { + fun newInstance(context: Context, sharedPrefProvider: SharedPrefProvider): AppDatabase { return Room.databaseBuilder(context, AppDatabase::class.java, "wulkanowy_database") .setJournalMode(TRUNCATE) .fallbackToDestructiveMigrationFrom(VERSION_SCHEMA + 1) .fallbackToDestructiveMigrationOnDowngrade() - .addMigrations(*getMigrations()) + .addMigrations(*getMigrations(sharedPrefProvider)) .build() } } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt index 6a9533959..4a4aaf74f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt @@ -18,6 +18,12 @@ class SharedPrefProvider @Inject constructor(private val sharedPref: SharedPrefe fun getLong(key: String, defaultValue: Long) = sharedPref.getLong(key, defaultValue) + fun getString(key: String, defaultValue: String): String = sharedPref.getString(key, defaultValue) ?: defaultValue + + fun putString(key: String, value: String, sync: Boolean = false) { + sharedPref.edit(sync) { putString(key, value) } + } + fun delete(key: String) { sharedPref.edit().remove(key).apply() } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt index 1221a7aab..3f69c61b1 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Grade.kt @@ -19,7 +19,7 @@ data class Grade( val entry: String, - val value: Int, + val value: Double, val modifier: Double, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt index 48b4fd022..93e254c3b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Message.kt @@ -29,6 +29,8 @@ data class Message( val subject: String, + var content: String, + val date: LocalDateTime, @ColumnInfo(name = "folder_id") @@ -50,6 +52,4 @@ data class Message( @ColumnInfo(name = "is_notified") var isNotified: Boolean = true - - var content: String? = null } 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 13c5ee084..fe0283f87 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 @@ -10,10 +10,27 @@ import java.io.Serializable @Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)]) data class Student( - val endpoint: String, + @ColumnInfo(name = "scrapper_base_url") + val scrapperBaseUrl: String, + @ColumnInfo(name = "mobile_base_url") + val mobileBaseUrl: String, + + @ColumnInfo(name = "login_type") val loginType: String, + @ColumnInfo(name = "login_mode") + val loginMode: String, + + @ColumnInfo(name = "certificate_key") + val certificateKey: String, + + @ColumnInfo(name = "private_key") + val privateKey: String, + + @ColumnInfo(name = "is_parent") + val isParent: Boolean, + val email: String, var password: String, @@ -23,6 +40,9 @@ data class Student( @ColumnInfo(name = "student_id") val studentId: Int, + @ColumnInfo(name = "user_login_id") + val userLoginId: Int, + @ColumnInfo(name = "student_name") val studentName: String, diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration19.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration19.kt new file mode 100644 index 000000000..d38f1245a --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration19.kt @@ -0,0 +1,119 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import io.github.wulkanowy.data.db.SharedPrefProvider + +class Migration19(private val sharedPrefProvider: SharedPrefProvider) : Migration(18, 19) { + + override fun migrate(database: SupportSQLiteDatabase) { + migrateMessages(database) + migrateGrades(database) + migrateStudents(database) + migrateSharedPreferences() + } + + private fun migrateMessages(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE Messages") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Messages ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + is_notified INTEGER NOT NULL, + student_id INTEGER NOT NULL, + real_id INTEGER NOT NULL, + message_id INTEGER NOT NULL, + sender_name TEXT NOT NULL, + sender_id INTEGER NOT NULL, + recipient_name TEXT NOT NULL, + subject TEXT NOT NULL, + content TEXT NOT NULL, + date INTEGER NOT NULL, + folder_id INTEGER NOT NULL, + unread INTEGER NOT NULL, + unread_by INTEGER NOT NULL, + read_by INTEGER NOT NULL, + removed INTEGER NOT NULL + ) + """) + } + + private fun migrateGrades(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE Grades") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Grades ( + 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 + ) + """) + } + + private fun migrateStudents(database: SupportSQLiteDatabase) { + database.execSQL(""" + CREATE TABLE IF NOT EXISTS Students_tmp ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + scrapper_base_url TEXT NOT NULL, + mobile_base_url TEXT NOT NULL, + is_parent INTEGER NOT NULL, + login_type TEXT NOT NULL, + login_mode TEXT NOT NULL, + certificate_key TEXT NOT NULL, + private_key TEXT NOT NULL, + email TEXT NOT NULL, + password TEXT NOT NULL, + symbol TEXT NOT NULL, + student_id INTEGER NOT NULL, + user_login_id INTEGER NOT NULL, + student_name TEXT NOT NULL, + school_id TEXT NOT NULL, + school_name TEXT NOT NULL, + class_name TEXT NOT NULL, + class_id INTEGER NOT NULL, + is_current INTEGER NOT NULL, + registration_date INTEGER NOT NULL + ) + """) + + database.execSQL("ALTER TABLE Students ADD COLUMN scrapperBaseUrl TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN apiBaseUrl TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN is_parent INT NOT NULL DEFAULT 0;") + database.execSQL("ALTER TABLE Students ADD COLUMN loginMode TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN certificateKey TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN privateKey TEXT NOT NULL DEFAULT \"\";") + database.execSQL("ALTER TABLE Students ADD COLUMN user_login_id INTEGER NOT NULL DEFAULT 0;") + + database.execSQL(""" + INSERT INTO Students_tmp( + id, scrapper_base_url, mobile_base_url, is_parent, login_type, login_mode, certificate_key, private_key, email, password, symbol, student_id, user_login_id, student_name, school_id, school_name, school_id, school_name, class_name, class_id, is_current, registration_date) + SELECT + id, endpoint, apiBaseUrl, is_parent, loginType, "SCRAPPER", certificateKey, privateKey, email, password, symbol, student_id, user_login_id, student_name, school_id, school_name, school_id, school_name, class_name, class_id, is_current, registration_date + FROM Students + """) + database.execSQL("DROP TABLE Students") + database.execSQL("ALTER TABLE Students_tmp RENAME TO Students") + database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students (email, symbol, student_id, school_id, class_id)") + } + + private fun migrateSharedPreferences() { + if (sharedPrefProvider.getString("grade_modifier_plus", "0.0") == "0.0") { + sharedPrefProvider.putString("grade_modifier_plus", "0.33") + } + if (sharedPrefProvider.getString("grade_modifier_minus", "0.0") == "0.0") { + sharedPrefProvider.putString("grade_modifier_minus", "0.33") + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt index b3544c3f5..cd7702c10 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt @@ -1,25 +1,24 @@ package io.github.wulkanowy.data.repositories.attendance -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import org.threeten.bp.LocalDate import javax.inject.Inject import javax.inject.Singleton @Singleton -class AttendanceRemote @Inject constructor(private val api: Api) { +class AttendanceRemote @Inject constructor(private val sdk: Sdk) { fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getAttendance(startDate, endDate) }.map { attendance -> + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getAttendance(startDate, endDate, semester.semesterId) + .map { attendance -> attendance.map { Attendance( studentId = semester.studentId, diaryId = semester.diaryId, - date = it.date.toLocalDate(), + date = it.date, number = it.number, subject = it.subject, name = it.name, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt index d38dd3a4b..c167427f7 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendancesummary/AttendanceSummaryRemote.kt @@ -1,18 +1,18 @@ package io.github.wulkanowy.data.repositories.attendancesummary -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @Singleton -class AttendanceSummaryRemote @Inject constructor(private val api: Api) { +class AttendanceSummaryRemote @Inject constructor(private val sdk: Sdk) { fun getAttendanceSummary(semester: Semester, subjectId: Int): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { api.getAttendanceSummary(subjectId) }.map { attendance -> + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getAttendanceSummary(subjectId) + .map { attendance -> attendance.map { AttendanceSummary( studentId = semester.studentId, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt index 58dd5a9d1..ca680f209 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemote.kt @@ -1,27 +1,25 @@ package io.github.wulkanowy.data.repositories.completedlessons -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.toLocalDate import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import org.threeten.bp.LocalDate import javax.inject.Inject import javax.inject.Singleton @Singleton -class CompletedLessonsRemote @Inject constructor(private val api: Api) { +class CompletedLessonsRemote @Inject constructor(private val sdk: Sdk) { fun getCompletedLessons(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getCompletedLessons(startDate, endDate) } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getCompletedLessons(startDate, endDate) .map { lessons -> lessons.map { it.absence CompletedLesson( studentId = semester.studentId, diaryId = semester.diaryId, - date = it.date.toLocalDate(), + date = it.date, number = it.number, subject = it.subject, topic = it.topic, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt index f6d653a61..ea7ec9441 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRemote.kt @@ -1,26 +1,25 @@ package io.github.wulkanowy.data.repositories.exam -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import org.threeten.bp.LocalDate import javax.inject.Inject import javax.inject.Singleton @Singleton -class ExamRemote @Inject constructor(private val api: Api) { +class ExamRemote @Inject constructor(private val sdk: Sdk) { fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getExams(startDate, endDate) }.map { exams -> + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getExams(startDate, endDate, semester.semesterId) + .map { exams -> exams.map { Exam( studentId = semester.studentId, diaryId = semester.diaryId, - date = it.date.toLocalDate(), - entryDate = it.entryDate.toLocalDate(), + date = it.date, + entryDate = it.entryDate, subject = it.subject, group = it.group, type = it.type, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt index 570ab7a77..d07ac6d1c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/grade/GradeRemote.kt @@ -1,35 +1,33 @@ package io.github.wulkanowy.data.repositories.grade -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @Singleton -class GradeRemote @Inject constructor(private val api: Api) { +class GradeRemote @Inject constructor(private val sdk: Sdk) { fun getGrades(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getGrades(semester.semesterId) } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGrades(semester.semesterId) .map { grades -> grades.map { Grade( - semesterId = semester.semesterId, studentId = semester.studentId, + semesterId = semester.semesterId, subject = it.subject, entry = it.entry, value = it.value, modifier = it.modifier, comment = it.comment, color = it.color, - gradeSymbol = it.symbol.orEmpty(), - description = it.description.orEmpty(), + gradeSymbol = it.symbol, + description = it.description, weight = it.weight, weightValue = it.weightValue, - date = it.date.toLocalDate(), + date = it.date, teacher = it.teacher ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt index 3335cfb02..2681fe058 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradessummary/GradeSummaryRemote.kt @@ -1,24 +1,23 @@ package io.github.wulkanowy.data.repositories.gradessummary -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @Singleton -class GradeSummaryRemote @Inject constructor(private val api: Api) { +class GradeSummaryRemote @Inject constructor(private val sdk: Sdk) { fun getGradeSummary(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getGradesSummary(semester.semesterId) } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGradesSummary(semester.semesterId) .map { gradesSummary -> gradesSummary.map { GradeSummary( semesterId = semester.semesterId, studentId = semester.studentId, - position = it.order, + position = 0, subject = it.name, predictedGrade = it.predicted, finalGrade = it.final, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt index 74c1f7357..f10f30369 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt @@ -1,39 +1,36 @@ package io.github.wulkanowy.data.repositories.gradestatistics -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @Singleton -class GradeStatisticsRemote @Inject constructor(private val api: Api) { +class GradeStatisticsRemote @Inject constructor(private val sdk: Sdk) { fun getGradeStatistics(semester: Semester, isSemester: Boolean): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { - if (isSemester) it.getGradesAnnualStatistics(semester.semesterId) - else it.getGradesPartialStatistics(semester.semesterId) - } - .map { gradeStatistics -> - gradeStatistics.map { - GradeStatistics( - semesterId = semester.semesterId, - studentId = semester.studentId, - subject = it.subject, - grade = it.gradeValue, - amount = it.amount ?: 0, - semester = isSemester - ) - } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).let { + if (isSemester) it.getGradesAnnualStatistics(semester.semesterId) + else it.getGradesPartialStatistics(semester.semesterId) + }.map { gradeStatistics -> + gradeStatistics.map { + GradeStatistics( + semesterId = semester.semesterId, + studentId = semester.studentId, + subject = it.subject, + grade = it.gradeValue, + amount = it.amount, + semester = isSemester + ) } + } } fun getGradePointsStatistics(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getGradesPointsStatistics(semester.semesterId) } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getGradesPointsStatistics(semester.semesterId) .map { gradePointsStatistics -> gradePointsStatistics.map { GradePointsStatistics( diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt index 681b66469..189499dbe 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/homework/HomeworkRemote.kt @@ -1,27 +1,25 @@ package io.github.wulkanowy.data.repositories.homework -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import org.threeten.bp.LocalDate import javax.inject.Inject import javax.inject.Singleton @Singleton -class HomeworkRemote @Inject constructor(private val api: Api) { +class HomeworkRemote @Inject constructor(private val sdk: Sdk) { fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getHomework(startDate, endDate) } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getHomework(startDate, endDate) .map { homework -> homework.map { Homework( semesterId = semester.semesterId, studentId = semester.studentId, - date = it.date.toLocalDate(), - entryDate = it.entryDate.toLocalDate(), + date = it.date, + entryDate = it.entryDate, subject = it.subject, content = it.content, teacher = it.teacher, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt index 1b0f12b3e..75454143f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemote.kt @@ -1,20 +1,18 @@ package io.github.wulkanowy.data.repositories.luckynumber -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Maybe -import io.reactivex.Single import org.threeten.bp.LocalDate import javax.inject.Inject import javax.inject.Singleton @Singleton -class LuckyNumberRemote @Inject constructor(private val api: Api) { +class LuckyNumberRemote @Inject constructor(private val sdk: Sdk) { fun getLuckyNumber(semester: Semester): Maybe { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMapMaybe { it.getLuckyNumber() } + return sdk.getLuckyNumber() .map { LuckyNumber( studentId = semester.studentId, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt index 90c510ef8..d27eb904d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRemote.kt @@ -1,23 +1,23 @@ package io.github.wulkanowy.data.repositories.message -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.messages.Folder -import io.github.wulkanowy.api.messages.SentMessage import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.toLocalDateTime +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.Folder +import io.github.wulkanowy.sdk.pojo.SentMessage import io.reactivex.Single import org.threeten.bp.LocalDateTime.now import javax.inject.Inject import javax.inject.Singleton -import io.github.wulkanowy.api.messages.Recipient as ApiRecipient +import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient @Singleton -class MessageRemote @Inject constructor(private val api: Api) { +class MessageRemote @Inject constructor(private val sdk: Sdk) { - fun getMessages(student: Student, folder: MessageFolder): Single> { - return api.getMessages(Folder.valueOf(folder.name)).map { messages -> + fun getMessages(student: Student, semester: Semester, folder: MessageFolder): Single> { + return sdk.getMessages(Folder.valueOf(folder.name), semester.start.atStartOfDay(), semester.end.atStartOfDay()).map { messages -> messages.map { Message( studentId = student.id.toInt(), @@ -27,7 +27,8 @@ class MessageRemote @Inject constructor(private val api: Api) { senderId = it.senderId ?: 0, recipient = it.recipient.orEmpty(), subject = it.subject.trim(), - date = it.date?.toLocalDateTime() ?: now(), + date = it.date ?: now(), + content = it.content.orEmpty(), folderId = it.folderId, unread = it.unread ?: false, unreadBy = it.unreadBy ?: 0, @@ -39,27 +40,28 @@ class MessageRemote @Inject constructor(private val api: Api) { } fun getMessagesContent(message: Message, markAsRead: Boolean = false): Single { - return api.getMessageContent(message.messageId, message.folderId, markAsRead, message.realId) + return sdk.getMessageContent(message.messageId, message.folderId, markAsRead, message.realId) } fun sendMessage(subject: String, content: String, recipients: List): Single { - return api.sendMessage( + return sdk.sendMessage( subject = subject, content = content, recipients = recipients.map { - ApiRecipient( + SdkRecipient( id = it.realId, name = it.realName, loginId = it.loginId, reportingUnitId = it.unitId, role = it.role, - hash = it.hash + hash = it.hash, + shortName = it.name ) } ) } fun deleteMessage(message: Message): Single { - return api.deleteMessages(listOf(Pair(message.realId, message.folderId))) + return sdk.deleteMessages(listOf(Pair(message.realId, message.folderId))) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt index c10cd5181..0dfdd72fe 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/message/MessageRepository.kt @@ -2,12 +2,13 @@ package io.github.wulkanowy.data.repositories.message import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.api.messages.SentMessage -import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Recipient +import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED +import io.github.wulkanowy.sdk.pojo.SentMessage import io.github.wulkanowy.utils.uniqueSubtract import io.reactivex.Completable import io.reactivex.Maybe @@ -21,16 +22,16 @@ class MessageRepository @Inject constructor( private val settings: InternetObservingSettings, private val local: MessageLocal, private val remote: MessageRemote, - private val apiHelper: ApiHelper + private val sdkHelper: SdkHelper ) { - fun getMessages(student: Student, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single> { - return Single.just(apiHelper.initApi(student)) + fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): Single> { + return Single.just(sdkHelper.init(student)) .flatMap { _ -> local.getMessages(student, folder).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { - if (it) remote.getMessages(student, folder) + if (it) remote.getMessages(student, semester, folder) else Single.error(UnknownHostException()) }.flatMap { new -> local.getMessages(student, folder).toSingle(emptyList()) @@ -47,10 +48,10 @@ class MessageRepository @Inject constructor( } fun getMessage(student: Student, messageDbId: Long, markAsRead: Boolean = false): Single { - return Single.just(apiHelper.initApi(student)) + return Single.just(sdkHelper.init(student)) .flatMap { _ -> local.getMessage(messageDbId) - .filter { !it.content.isNullOrEmpty() } + .filter { it.content.isNotEmpty() } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { if (it) local.getMessage(messageDbId).toSingle() @@ -60,7 +61,7 @@ class MessageRepository @Inject constructor( remote.getMessagesContent(dbMessage, markAsRead).doOnSuccess { local.updateMessages(listOf(dbMessage.copy(unread = false).apply { id = dbMessage.id - content = it + content = content.ifBlank { it } })) } }.flatMap { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRemote.kt index 86fdce97a..c43c3f21e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/mobiledevice/MobileDeviceRemote.kt @@ -1,25 +1,23 @@ package io.github.wulkanowy.data.repositories.mobiledevice -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.MobileDevice import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.pojos.MobileDeviceToken -import io.github.wulkanowy.utils.toLocalDateTime +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @Singleton -class MobileDeviceRemote @Inject constructor(private val api: Api) { +class MobileDeviceRemote @Inject constructor(private val sdk: Sdk) { fun getDevices(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { api.getRegisteredDevices() } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getRegisteredDevices() .map { devices -> devices.map { MobileDevice( studentId = semester.studentId, - date = it.date.toLocalDateTime(), + date = it.date, deviceId = it.id, name = it.name ) @@ -28,13 +26,11 @@ class MobileDeviceRemote @Inject constructor(private val api: Api) { } fun unregisterDevice(semester: Semester, device: MobileDevice): Single { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { api.unregisterDevice(device.deviceId) } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).unregisterDevice(device.deviceId) } fun getToken(semester: Semester): Single { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { api.getToken() } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getToken() .map { MobileDeviceToken( token = it.token, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt index aebc6230e..503575b69 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/note/NoteRemote.kt @@ -1,24 +1,22 @@ package io.github.wulkanowy.data.repositories.note -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.utils.toLocalDate +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @Singleton -class NoteRemote @Inject constructor(private val api: Api) { +class NoteRemote @Inject constructor(private val sdk: Sdk) { fun getNotes(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getNotes() } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getNotes(semester.semesterId) .map { notes -> notes.map { Note( studentId = semester.studentId, - date = it.date.toLocalDate(), + date = it.date, teacher = it.teacher, category = it.category, content = it.content diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt index b726edda9..f070ea765 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRemote.kt @@ -1,34 +1,34 @@ package io.github.wulkanowy.data.repositories.recipient -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton -import io.github.wulkanowy.api.messages.Recipient as ApiRecipient +import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient @Singleton -class RecipientRemote @Inject constructor(private val api: Api) { +class RecipientRemote @Inject constructor(private val sdk: Sdk) { fun getRecipients(role: Int, unit: ReportingUnit): Single> { - return api.getRecipients(unit.realId, role) + return sdk.getRecipients(unit.realId, role) .map { recipients -> recipients.map { it.toRecipient() } } } fun getMessageRecipients(message: Message): Single> { - return api.getMessageRecipients(message.messageId, message.senderId) + return sdk.getMessageRecipients(message.messageId, message.senderId) .map { recipients -> recipients.map { it.toRecipient() } } } - private fun ApiRecipient.toRecipient(): Recipient { + private fun SdkRecipient.toRecipient(): Recipient { return Recipient( - studentId = api.studentId, + studentId = sdk.studentId, realId = id, realName = name, name = shortName, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt index cde75ea8b..d29369b94 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/recipient/RecipientRepository.kt @@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.recipient import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.ReportingUnit @@ -18,11 +18,11 @@ class RecipientRepository @Inject constructor( private val settings: InternetObservingSettings, private val local: RecipientLocal, private val remote: RecipientRemote, - private val apiHelper: ApiHelper + private val sdkHelper: SdkHelper ) { fun getRecipients(student: Student, role: Int, unit: ReportingUnit, forceRefresh: Boolean = false): Single> { - return Single.just(apiHelper.initApi(student)) + return Single.just(sdkHelper.init(student)) .flatMap { _ -> local.getRecipients(student, role, unit).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) @@ -43,7 +43,7 @@ class RecipientRepository @Inject constructor( } fun getMessageRecipients(student: Student, message: Message): Single> { - return Single.just(apiHelper.initApi(student)) + return Single.just(sdkHelper.init(student)) .flatMap { ReactiveNetwork.checkInternetConnectivity(settings) } .flatMap { if (it) remote.getMessageRecipients(message) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt index feb4b0134..5dbabc545 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRemote.kt @@ -1,19 +1,19 @@ package io.github.wulkanowy.data.repositories.reportingunit -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.ReportingUnit +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @Singleton -class ReportingUnitRemote @Inject constructor(private val api: Api) { +class ReportingUnitRemote @Inject constructor(private val sdk: Sdk) { fun getReportingUnits(): Single> { - return api.getReportingUnits().map { + return sdk.getReportingUnits().map { it.map { unit -> ReportingUnit( - studentId = api.studentId, + studentId = sdk.studentId, realId = unit.id, roles = unit.roles, senderId = unit.senderId, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt index 6758898e2..4c8370e68 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/reportingunit/ReportingUnitRepository.kt @@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.reportingunit import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.utils.uniqueSubtract @@ -17,11 +17,11 @@ class ReportingUnitRepository @Inject constructor( private val settings: InternetObservingSettings, private val local: ReportingUnitLocal, private val remote: ReportingUnitRemote, - private val apiHelper: ApiHelper + private val sdkHelper: SdkHelper ) { fun getReportingUnits(student: Student, forceRefresh: Boolean = false): Single> { - return Single.just(apiHelper.initApi(student)) + return Single.just(sdkHelper.init(student)) .flatMap { _ -> local.getReportingUnits(student).filter { !forceRefresh } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) @@ -40,7 +40,7 @@ class ReportingUnitRepository @Inject constructor( } fun getReportingUnit(student: Student, unitId: Int): Maybe { - return Maybe.just(apiHelper.initApi(student)) + return Maybe.just(sdkHelper.init(student)) .flatMap { _ -> local.getReportingUnit(student, unitId) .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolRemote.kt index 86e05f0f4..4d5c92a9e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/school/SchoolRemote.kt @@ -1,16 +1,15 @@ package io.github.wulkanowy.data.repositories.school -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.School import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject -class SchoolRemote @Inject constructor(private val api: Api) { +class SchoolRemote @Inject constructor(private val sdk: Sdk) { fun getSchoolInfo(semester: Semester): Single { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getSchool() } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getSchool() .map { School( studentId = semester.studentId, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt index c199c16c0..55d13a765 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRemote.kt @@ -1,35 +1,32 @@ package io.github.wulkanowy.data.repositories.semester -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @Singleton -class SemesterRemote @Inject constructor(private val api: Api) { +class SemesterRemote @Inject constructor(private val sdk: Sdk) { fun getSemesters(student: Student): Single> { - return api.getSemesters().map { semesters -> - semesters.map { semester -> + return sdk.getSemesters().map { semesters -> + semesters.map { Semester( studentId = student.studentId, - diaryId = semester.diaryId, - diaryName = semester.diaryName, - schoolYear = semester.schoolYear, - semesterId = semester.semesterId, - semesterName = semester.semesterNumber, - isCurrent = semester.current, - start = semester.start, - end = semester.end, - classId = semester.classId, - unitId = semester.unitId + diaryId = it.diaryId, + diaryName = it.diaryName, + schoolYear = it.schoolYear, + semesterId = it.semesterId, + semesterName = it.semesterNumber, + isCurrent = it.current, + start = it.start, + end = it.end, + classId = it.classId, + unitId = it.unitId ) } - } } } - - diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt index 593014032..95fddc1b3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt @@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.semester import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.utils.uniqueSubtract @@ -18,11 +18,11 @@ class SemesterRepository @Inject constructor( private val remote: SemesterRemote, private val local: SemesterLocal, private val settings: InternetObservingSettings, - private val apiHelper: ApiHelper + private val sdkHelper: SdkHelper ) { fun getSemesters(student: Student, forceRefresh: Boolean = false): Single> { - return Maybe.just(apiHelper.initApi(student)) + return Maybe.just(sdkHelper.init(student)) .flatMap { local.getSemesters(student).filter { !forceRefresh } } .switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) .flatMap { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt index f27199f28..a3576ca22 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentLocal.kt @@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.student import android.content.Context import io.github.wulkanowy.data.db.dao.StudentDao import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.security.decrypt import io.github.wulkanowy.utils.security.encrypt import io.reactivex.Completable @@ -18,7 +19,12 @@ class StudentLocal @Inject constructor( ) { fun saveStudents(students: List): Single> { - return Single.fromCallable { studentDb.insertAll(students.map { it.copy(password = encrypt(it.password, context)) }) } + return Single.fromCallable { + studentDb.insertAll(students.map { + if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) it.copy(password = encrypt(it.password, context)) + else it + }) + } } fun getStudents(decryptPass: Boolean): Maybe> { @@ -28,7 +34,11 @@ class StudentLocal @Inject constructor( } fun getCurrentStudent(decryptPass: Boolean): Maybe { - return studentDb.loadCurrent().map { it.apply { if (decryptPass) password = decrypt(password) } } + return studentDb.loadCurrent().map { + it.apply { + if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password) + } + } } fun setCurrentStudent(student: Student): Completable { 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 index 251d38344..944313082 100644 --- 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 @@ -1,34 +1,51 @@ package io.github.wulkanowy.data.repositories.student -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import org.threeten.bp.LocalDateTime.now import javax.inject.Inject import javax.inject.Singleton +import io.github.wulkanowy.sdk.pojo.Student as SdkStudent @Singleton -class StudentRemote @Inject constructor(private val api: Api) { +class StudentRemote @Inject constructor(private val sdk: Sdk) { - fun getStudents(email: String, password: String, endpoint: String): Single> { - return api.getStudents().map { students -> - students.map { student -> - Student( - email = email, - password = password, - symbol = student.symbol, - studentId = student.studentId, - studentName = student.studentName, - schoolSymbol = student.schoolSymbol, - schoolName = student.schoolName, - className = student.className, - classId = student.classId, - endpoint = endpoint, - loginType = student.loginType.name, - isCurrent = false, - registrationDate = now() - ) - } + private fun mapStudents(students: List, email: String, password: String): List { + return students.map { student -> + Student( + email = email, + password = password, + isParent = student.isParent, + symbol = student.symbol, + studentId = student.studentId, + userLoginId = student.userLoginId, + studentName = student.studentName, + schoolSymbol = student.schoolSymbol, + schoolName = student.schoolName, + className = student.className, + classId = student.classId, + scrapperBaseUrl = student.scrapperBaseUrl, + loginType = student.loginType.name, + isCurrent = false, + registrationDate = now(), + mobileBaseUrl = student.mobileBaseUrl, + privateKey = student.privateKey, + certificateKey = student.certificateKey, + loginMode = student.loginMode.name + ) } } + + fun getStudentsMobileApi(token: String, pin: String, symbol: String): Single> { + return sdk.getStudentsFromMobileApi(token, pin, symbol).map { mapStudents(it, "", "") } + } + + fun getStudentsScrapper(email: String, password: String, scrapperBaseUrl: String, symbol: String): Single> { + return sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol).map { mapStudents(it, email, password) } + } + + fun getStudentsHybrid(email: String, password: String, scrapperBaseUrl: String, symbol: String): Single> { + return sdk.getStudentsHybrid(email, password, scrapperBaseUrl, symbol).map { mapStudents(it, email, password) } + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt index 5c4a60558..e06e654a9 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/student/StudentRepository.kt @@ -2,7 +2,6 @@ package io.github.wulkanowy.data.repositories.student import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.exceptions.NoCurrentStudentException import io.reactivex.Completable @@ -16,21 +15,32 @@ import javax.inject.Singleton class StudentRepository @Inject constructor( private val local: StudentLocal, private val remote: StudentRemote, - private val settings: InternetObservingSettings, - private val apiHelper: ApiHelper + private val settings: InternetObservingSettings ) { fun isStudentSaved(): Single = local.getStudents(false).isEmpty.map { !it } fun isCurrentStudentSet(): Single = local.getCurrentStudent(false).isEmpty.map { !it } - fun getStudents(email: String, password: String, endpoint: String, symbol: String = ""): Single> { - return ReactiveNetwork.checkInternetConnectivity(settings) - .flatMap { - apiHelper.initApi(email, password, symbol, endpoint) - if (it) remote.getStudents(email, password, endpoint) - else Single.error(UnknownHostException("No internet connection")) - } + fun getStudentsApi(pin: String, symbol: String, token: String): Single> { + return ReactiveNetwork.checkInternetConnectivity(settings).flatMap { + if (it) remote.getStudentsMobileApi(token, pin, symbol) + else Single.error(UnknownHostException("No internet connection")) + } + } + + fun getStudentsScrapper(email: String, password: String, endpoint: String, symbol: String = ""): Single> { + return ReactiveNetwork.checkInternetConnectivity(settings).flatMap { + if (it) remote.getStudentsScrapper(email, password, endpoint, symbol) + else Single.error(UnknownHostException("No internet connection")) + } + } + + fun getStudentsHybrid(email: String, password: String, endpoint: String, symbol: String): Single> { + return ReactiveNetwork.checkInternetConnectivity(settings).flatMap { + if (it) remote.getStudentsHybrid(email, password, endpoint, symbol) + else Single.error(UnknownHostException("No internet connection")) + } } fun getSavedStudents(decryptPass: Boolean = true): Single> { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt index 88fbb196b..32dbb2f2e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/subject/SubjectRemote.kt @@ -1,25 +1,24 @@ package io.github.wulkanowy.data.repositories.subject -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Subject +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @Singleton -class SubjectRemote @Inject constructor(private val api: Api) { +class SubjectRemote @Inject constructor(private val sdk: Sdk) { fun getSubjects(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { api.getSubjects() } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getSubjects() .map { subjects -> subjects.map { Subject( studentId = semester.studentId, diaryId = semester.diaryId, name = it.name, - realId = it.value + realId = it.id ) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherRemote.kt index 0ae1cdb1c..d4401bfb2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/teacher/TeacherRemote.kt @@ -1,18 +1,17 @@ package io.github.wulkanowy.data.repositories.teacher -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Teacher +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import javax.inject.Inject import javax.inject.Singleton @Singleton -class TeacherRemote @Inject constructor(private val api: Api) { +class TeacherRemote @Inject constructor(private val sdk: Sdk) { fun getTeachers(semester: Semester): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getTeachers() } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getTeachers(semester.semesterId) .map { teachers -> teachers.map { Teacher( diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt index 77742e7b3..6036bd405 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt @@ -1,30 +1,27 @@ package io.github.wulkanowy.data.repositories.timetable -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Timetable -import io.github.wulkanowy.utils.toLocalDate -import io.github.wulkanowy.utils.toLocalDateTime +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import org.threeten.bp.LocalDate import javax.inject.Inject import javax.inject.Singleton @Singleton -class TimetableRemote @Inject constructor(private val api: Api) { +class TimetableRemote @Inject constructor(private val sdk: Sdk) { fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Single> { - return Single.just(api.apply { diaryId = semester.diaryId }) - .flatMap { it.getTimetable(startDate, endDate) } + return sdk.switchDiary(semester.diaryId, semester.schoolYear).getTimetable(startDate, endDate) .map { lessons -> lessons.map { Timetable( studentId = semester.studentId, diaryId = semester.diaryId, number = it.number, - start = it.start.toLocalDateTime(), - end = it.end.toLocalDateTime(), - date = it.date.toLocalDate(), + start = it.start, + end = it.end, + date = it.date, subject = it.subject, subjectOld = it.subjectOld, group = it.group, diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt index 192698999..68702d9a9 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncWorker.kt @@ -11,10 +11,10 @@ import androidx.work.WorkerParameters import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject import io.github.wulkanowy.R -import io.github.wulkanowy.api.interceptor.FeatureDisabledException import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.sdk.exception.FeatureDisabledException import io.github.wulkanowy.services.sync.channels.DebugChannel import io.github.wulkanowy.services.sync.works.Work import io.github.wulkanowy.utils.getCompatColor @@ -78,4 +78,3 @@ class SyncWorker @AssistedInject constructor( fun create(appContext: Context, workerParameters: WorkerParameters): ListenableWorker } } - diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt index c94f3aaf2..d0890626f 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/MessageWork.kt @@ -30,7 +30,7 @@ class MessageWork @Inject constructor( ) : Work { override fun create(student: Student, semester: Semester): Completable { - return messageRepository.getMessages(student, RECEIVED, true, preferencesRepository.isNotificationsEnable) + return messageRepository.getMessages(student, semester, RECEIVED, true, preferencesRepository.isNotificationsEnable) .flatMap { messageRepository.getNotNotifiedMessages(student) } .flatMapCompletable { if (it.isNotEmpty()) notify(it) diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt index ab67d8acb..ee74832fd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt @@ -1,10 +1,10 @@ package io.github.wulkanowy.ui.base import android.app.ActivityManager -import android.os.Build.VERSION.SDK_INT -import android.os.Build.VERSION_CODES.LOLLIPOP import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.os.Build.VERSION.SDK_INT +import android.os.Build.VERSION_CODES.LOLLIPOP import android.os.Bundle import android.view.View import android.widget.Toast diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt index 16e9a0480..ba10af6bf 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt @@ -3,11 +3,12 @@ package io.github.wulkanowy.ui.base import android.content.res.Resources import com.readystatesoftware.chuck.api.ChuckCollector import io.github.wulkanowy.R -import io.github.wulkanowy.api.interceptor.FeatureDisabledException -import io.github.wulkanowy.api.interceptor.ServiceUnavailableException -import io.github.wulkanowy.api.login.BadCredentialsException -import io.github.wulkanowy.api.login.NotLoggedInException import io.github.wulkanowy.data.exceptions.NoCurrentStudentException +import io.github.wulkanowy.sdk.exception.BadCredentialsException +import io.github.wulkanowy.sdk.exception.FeatureDisabledException +import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException +import io.github.wulkanowy.sdk.exception.NotLoggedInException +import io.github.wulkanowy.sdk.exception.ServiceUnavailableException import io.github.wulkanowy.utils.security.ScramblerException import timber.log.Timber import java.net.SocketTimeoutException @@ -38,6 +39,7 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources, is FeatureDisabledException -> showErrorMessage(getString(R.string.error_feature_disabled), error) is ScramblerException, is BadCredentialsException -> onSessionExpired() is NoCurrentStudentException -> onNoCurrentStudent() + is FeatureNotAvailableException -> showErrorMessage(getString(R.string.error_feature_not_available), error) else -> showErrorMessage(getString(R.string.error_unknown), error) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt index 3f794ff1f..88635c37b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt @@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.grade.GradeRepository import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.calcAverage import io.github.wulkanowy.utils.changeModifier import io.reactivex.Maybe @@ -40,7 +41,7 @@ class GradeAverageProvider @Inject constructor( .map { secondGrades -> secondGrades + firstGrades } } }.map { grades -> - grades.map { it.changeModifier(plusModifier, minusModifier) } + grades.map { if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) else it } .groupBy { it.subject } .mapValues { it.value.calcAverage() } }) @@ -54,7 +55,7 @@ class GradeAverageProvider @Inject constructor( return getAverageFromGradeSummary(selectedSemester, forceRefresh) .switchIfEmpty(gradeRepository.getGrades(student, selectedSemester, forceRefresh) .map { grades -> - grades.map { it.changeModifier(plusModifier, minusModifier) } + grades.map { if (student.loginMode == Sdk.Mode.SCRAPPER.name) it.changeModifier(plusModifier, minusModifier) else it } .groupBy { it.subject } .mapValues { it.value.calcAverage() } }) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt index 884b9f8c4..e8d970010 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeFragment.kt @@ -13,7 +13,6 @@ import androidx.appcompat.app.AlertDialog import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter -import io.github.wulkanowy.ui.base.ErrorDialog import io.github.wulkanowy.ui.modules.grade.details.GradeDetailsFragment import io.github.wulkanowy.ui.modules.grade.statistics.GradeStatisticsFragment import io.github.wulkanowy.ui.modules.grade.summary.GradeSummaryFragment diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt index b3dd35878..195ae11df 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsFragment.kt @@ -18,7 +18,6 @@ import eu.davidea.flexibleadapter.items.IFlexible import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.base.ErrorDialog import io.github.wulkanowy.ui.modules.grade.GradeFragment import io.github.wulkanowy.ui.modules.grade.GradeView import io.github.wulkanowy.ui.modules.main.MainActivity diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt index 6043d50eb..455d1e8ec 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt @@ -8,6 +8,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.ui.base.BaseActivity import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment @@ -50,7 +51,8 @@ class LoginActivity : BaseActivity(), LoginView { addFragments(listOf( LoginFormFragment.newInstance(), LoginSymbolFragment.newInstance(), - LoginStudentSelectFragment.newInstance() + LoginStudentSelectFragment.newInstance(), + LoginAdvancedFragment.newInstance() )) } @@ -93,4 +95,8 @@ class LoginActivity : BaseActivity(), LoginView { fun onSymbolFragmentAccountLogged(students: List) { presenter.onSymbolViewAccountLogged(students) } + + fun onAdvancedLoginClick() { + presenter.onAdvancedLoginClick() + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt index 76832bdac..c888ce79b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt @@ -4,7 +4,7 @@ import android.content.res.Resources import android.database.sqlite.SQLiteConstraintException import com.readystatesoftware.chuck.api.ChuckCollector import io.github.wulkanowy.R -import io.github.wulkanowy.api.login.BadCredentialsException +import io.github.wulkanowy.sdk.exception.BadCredentialsException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt index 8bcf042b5..28339a467 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginModule.kt @@ -6,6 +6,7 @@ import dagger.android.ContributesAndroidInjector import io.github.wulkanowy.di.scopes.PerActivity import io.github.wulkanowy.di.scopes.PerFragment import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter +import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment @@ -26,6 +27,10 @@ internal abstract class LoginModule { @ContributesAndroidInjector abstract fun bindLoginFormFragment(): LoginFormFragment + @PerFragment + @ContributesAndroidInjector + abstract fun bindLoginAdvancedFragment(): LoginAdvancedFragment + @PerFragment @ContributesAndroidInjector abstract fun bindLoginSymbolFragment(): LoginSymbolFragment diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt index 8ff5e4daf..afa2d145d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt @@ -45,6 +45,10 @@ class LoginPresenter @Inject constructor( } } + fun onAdvancedLoginClick() { + view?.switchView(3) + } + fun onViewSelected(index: Int) { view?.apply { when (index) { @@ -58,7 +62,7 @@ class LoginPresenter @Inject constructor( Timber.i("Back pressed in login view") view?.apply { when (currentViewIndex) { - 1, 2 -> switchView(0) + 1, 2, 3 -> switchView(0) else -> default() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt new file mode 100644 index 000000000..30563ebf4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt @@ -0,0 +1,227 @@ +package io.github.wulkanowy.ui.modules.login.advanced + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import androidx.core.widget.doOnTextChanged +import io.github.wulkanowy.R +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.login.LoginActivity +import io.github.wulkanowy.ui.modules.login.form.LoginSymbolAdapter +import io.github.wulkanowy.utils.hideSoftInput +import io.github.wulkanowy.utils.showSoftInput +import kotlinx.android.synthetic.main.fragment_login_advanced.* +import javax.inject.Inject + +class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { + + @Inject + lateinit var presenter: LoginAdvancedPresenter + + companion object { + fun newInstance() = LoginAdvancedFragment() + } + + override val formLoginType: String + get() = when (loginTypeSwitch.checkedRadioButtonId) { + R.id.loginTypeApi -> "API" + R.id.loginTypeScrapper -> "SCRAPPER" + else -> "HYBRID" + } + + override val formNameValue: String + get() = loginFormName.text.toString().trim() + + override val formPassValue: String + get() = loginFormPass.text.toString().trim() + + private lateinit var hostKeys: Array + + private lateinit var hostValues: Array + + override val formHostValue: String? + get() = hostValues.getOrNull(hostKeys.indexOf(loginFormHost.text.toString())) + + override val formPinValue: String + get() = loginFormPin.text.toString().trim() + + override val formSymbolValue: String + get() = loginFormSymbol.text.toString().trim() + + override val formTokenValue: String + get() = loginFormToken.text.toString().trim() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_login_advanced, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + hostKeys = resources.getStringArray(R.array.hosts_keys) + hostValues = resources.getStringArray(R.array.hosts_values) + + loginFormName.doOnTextChanged { _, _, _, _ -> presenter.onNameTextChanged() } + loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() } + loginFormPin.doOnTextChanged { _, _, _, _ -> presenter.onPinTextChanged() } + loginFormSymbol.doOnTextChanged { _, _, _, _ -> presenter.onSymbolTextChanged() } + loginFormToken.doOnTextChanged { _, _, _, _ -> presenter.onTokenTextChanged() } + loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() } + loginFormSignIn.setOnClickListener { presenter.onSignInClick() } + + loginTypeSwitch.setOnCheckedChangeListener { _, checkedId -> + presenter.onLoginModeSelected(when (checkedId) { + R.id.loginTypeApi -> Sdk.Mode.API + R.id.loginTypeScrapper -> Sdk.Mode.SCRAPPER + else -> Sdk.Mode.HYBRID + }) + } + + loginFormSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values))) + + with(loginFormHost) { + setText(hostKeys.getOrElse(0) { "" }) + setAdapter(LoginSymbolAdapter(context, R.layout.support_simple_spinner_dropdown_item, hostKeys)) + } + } + + override fun setDefaultCredentials(name: String, pass: String, symbol: String, token: String, pin: String) { + loginFormName.setText(name) + loginFormPass.setText(pass) + loginFormToken.setText(token) + loginFormSymbol.setText(symbol) + loginFormPin.setText(pin) + } + + override fun setErrorNameRequired() { + loginFormNameLayout.run { + requestFocus() + error = getString(R.string.login_field_required) + } + } + + override fun setErrorPassRequired(focus: Boolean) { + loginFormPassLayout.run { + if (focus) requestFocus() + error = getString(R.string.login_field_required) + } + } + + override fun setErrorPassInvalid(focus: Boolean) { + loginFormPassLayout.run { + if (focus) requestFocus() + error = getString(R.string.login_invalid_password) + } + } + + override fun setErrorPassIncorrect() { + loginFormPassLayout.run { + requestFocus() + error = getString(R.string.login_incorrect_password) + } + } + + override fun setErrorPinRequired() { + loginFormPinLayout.run { + requestFocus() + error = getString(R.string.login_field_required) + } + } + + override fun setErrorSymbolRequired() { + loginFormSymbolLayout.run { + requestFocus() + error = getString(R.string.login_field_required) + } + } + + override fun setErrorTokenRequired() { + loginFormTokenLayout.run { + requestFocus() + error = getString(R.string.login_field_required) + } + } + + override fun clearNameError() { + loginFormNameLayout.error = null + } + + override fun clearPassError() { + loginFormPassLayout.error = null + } + + override fun clearPinKeyError() { + loginFormPinLayout.error = null + } + + override fun clearSymbolError() { + loginFormSymbolLayout.error = null + } + + override fun clearTokenError() { + loginFormTokenLayout.error = null + } + + override fun showOnlyHybridModeInputs() { + loginFormNameLayout.visibility = View.VISIBLE + loginFormPassLayout.visibility = View.VISIBLE + loginFormHostLayout.visibility = View.VISIBLE + loginFormPinLayout.visibility = View.GONE + loginFormSymbolLayout.visibility = View.VISIBLE + loginFormTokenLayout.visibility = View.GONE + } + + override fun showOnlyScrapperModeInputs() { + loginFormNameLayout.visibility = View.VISIBLE + loginFormPassLayout.visibility = View.VISIBLE + loginFormHostLayout.visibility = View.VISIBLE + loginFormPinLayout.visibility = View.GONE + loginFormSymbolLayout.visibility = View.VISIBLE + loginFormTokenLayout.visibility = View.GONE + } + + override fun showOnlyMobileApiModeInputs() { + loginFormNameLayout.visibility = View.GONE + loginFormPassLayout.visibility = View.GONE + loginFormHostLayout.visibility = View.GONE + loginFormPinLayout.visibility = View.VISIBLE + loginFormSymbolLayout.visibility = View.VISIBLE + loginFormTokenLayout.visibility = View.VISIBLE + } + + override fun showSoftKeyboard() { + activity?.showSoftInput() + } + + override fun hideSoftKeyboard() { + activity?.hideSoftInput() + } + + override fun showProgress(show: Boolean) { + loginFormProgress.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun showContent(show: Boolean) { + loginFormContainer.visibility = if (show) View.VISIBLE else View.GONE + } + + override fun notifyParentAccountLogged(students: List) { + (activity as? LoginActivity)?.onFormFragmentAccountLogged(students, Triple( + loginFormName.text.toString(), + loginFormPass.text.toString(), + resources.getStringArray(R.array.hosts_values)[1] + )) + } + + override fun onDestroyView() { + super.onDestroyView() + presenter.onDetachView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt new file mode 100644 index 000000000..2cd788b03 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt @@ -0,0 +1,185 @@ +package io.github.wulkanowy.ui.modules.login.advanced + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.modules.login.LoginErrorHandler +import io.github.wulkanowy.utils.FirebaseAnalyticsHelper +import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.ifNullOrBlank +import io.reactivex.Single +import timber.log.Timber +import javax.inject.Inject + +class LoginAdvancedPresenter @Inject constructor( + schedulers: SchedulersProvider, + studentRepository: StudentRepository, + private val loginErrorHandler: LoginErrorHandler, + private val analytics: FirebaseAnalyticsHelper +) : BasePresenter(loginErrorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: LoginAdvancedView) { + super.onAttachView(view) + view.run { + initView() + showOnlyScrapperModeInputs() + loginErrorHandler.onBadCredentials = { + setErrorPassIncorrect() + showSoftKeyboard() + Timber.i("Entered wrong username or password") + } + } + } + + fun onHostSelected() { + view?.apply { + clearPassError() + clearNameError() + if (formHostValue?.contains("fakelog") == true) { + setDefaultCredentials("jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999") + } + } + } + + fun onLoginModeSelected(type: Sdk.Mode) { + view?.run { + when (type) { + Sdk.Mode.API -> showOnlyMobileApiModeInputs() + Sdk.Mode.SCRAPPER -> showOnlyScrapperModeInputs() + Sdk.Mode.HYBRID -> showOnlyHybridModeInputs() + } + } + } + + fun onPassTextChanged() { + view?.clearPassError() + } + + fun onNameTextChanged() { + view?.clearNameError() + } + + fun onPinTextChanged() { + view?.clearPinKeyError() + } + + fun onSymbolTextChanged() { + view?.clearSymbolError() + } + + fun onTokenTextChanged() { + view?.clearTokenError() + } + + fun onSignInClick() { + if (!validateCredentials()) return + + disposable.add(getStudentsAppropriatesToLoginType() + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.apply { + hideSoftKeyboard() + showProgress(true) + showContent(false) + } + Timber.i("Login started") + } + .doFinally { + view?.apply { + showProgress(false) + showContent(true) + } + } + .subscribe({ + Timber.i("Login result: Success") + analytics.logEvent("registration_form", "success" to true, "students" to it.size, "error" to "No error") + view?.notifyParentAccountLogged(it) + }, { + Timber.i("Login result: An exception occurred") + analytics.logEvent("registration_form", "success" to false, "students" to -1, "error" to it.message.ifNullOrBlank { "No message" }) + loginErrorHandler.dispatch(it) + })) + } + + private fun getStudentsAppropriatesToLoginType(): Single> { + val email = view?.formNameValue.orEmpty() + val password = view?.formPassValue.orEmpty() + val endpoint = view?.formHostValue.orEmpty() + + val pin = view?.formPinValue.orEmpty() + val symbol = view?.formSymbolValue.orEmpty() + val token = view?.formTokenValue.orEmpty() + + return when (Sdk.Mode.valueOf(view?.formLoginType ?: "")) { + Sdk.Mode.API -> studentRepository.getStudentsApi(pin, symbol, token) + Sdk.Mode.SCRAPPER -> studentRepository.getStudentsScrapper(email, password, endpoint, symbol) + Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid(email, password, endpoint, symbol) + } + } + + private fun validateCredentials(): Boolean { + val login = view?.formNameValue.orEmpty() + val password = view?.formPassValue.orEmpty() + + val pin = view?.formPinValue.orEmpty() + val symbol = view?.formSymbolValue.orEmpty() + val token = view?.formTokenValue.orEmpty() + + var isCorrect = true + + when (Sdk.Mode.valueOf(view?.formLoginType ?: "")) { + Sdk.Mode.API -> { + if (pin.isEmpty()) { + view?.setErrorPinRequired() + isCorrect = false + } + + if (symbol.isEmpty()) { + view?.setErrorSymbolRequired() + isCorrect = false + } + + if (token.isEmpty()) { + view?.setErrorTokenRequired() + isCorrect = false + } + } + Sdk.Mode.SCRAPPER -> { + if (login.isEmpty()) { + view?.setErrorNameRequired() + isCorrect = false + } + + if (password.isEmpty()) { + view?.setErrorPassRequired(focus = isCorrect) + isCorrect = false + } + + if (password.length < 6 && password.isNotEmpty()) { + view?.setErrorPassInvalid(focus = isCorrect) + isCorrect = false + } + } + Sdk.Mode.HYBRID -> { + if (login.isEmpty()) { + view?.setErrorNameRequired() + isCorrect = false + } + + if (password.isEmpty()) { + view?.setErrorPassRequired(focus = isCorrect) + isCorrect = false + } + + if (password.length < 6 && password.isNotEmpty()) { + view?.setErrorPassInvalid(focus = isCorrect) + isCorrect = false + } + } + } + + return isCorrect + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt new file mode 100644 index 000000000..ea48d6464 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt @@ -0,0 +1,65 @@ +package io.github.wulkanowy.ui.modules.login.advanced + +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.ui.base.BaseView + +interface LoginAdvancedView : BaseView { + + val formNameValue: String + + val formPassValue: String + + val formHostValue: String? + + val formLoginType: String + + val formPinValue: String + + val formSymbolValue: String + + val formTokenValue: String + + fun initView() + + fun setDefaultCredentials(name: String, pass: String, symbol: String, token: String, pin: String) + + fun setErrorNameRequired() + + fun setErrorPassRequired(focus: Boolean) + + fun setErrorPassInvalid(focus: Boolean) + + fun setErrorPassIncorrect() + + fun clearNameError() + + fun clearPassError() + + fun clearPinKeyError() + + fun clearSymbolError() + + fun clearTokenError() + + fun showSoftKeyboard() + + fun hideSoftKeyboard() + + fun showProgress(show: Boolean) + + fun showContent(show: Boolean) + + fun notifyParentAccountLogged(students: List) + + fun setErrorPinRequired() + + fun setErrorSymbolRequired() + + fun setErrorTokenRequired() + + fun showOnlyHybridModeInputs() + + fun showOnlyScrapperModeInputs() + + fun showOnlyMobileApiModeInputs() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt index a3e704898..cbd405c28 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormFragment.kt @@ -61,6 +61,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView { loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() } loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() } loginFormSignIn.setOnClickListener { presenter.onSignInClick() } + loginFormAdvancedButton.setOnClickListener { presenter.onAdvancedLoginClick() } loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() } loginFormFaq.setOnClickListener { presenter.onFaqClick() } loginFormContactEmail.setOnClickListener { presenter.onEmailClick() } @@ -134,14 +135,7 @@ class LoginFormFragment : BaseFragment(), LoginFormView { @SuppressLint("SetTextI18n") override fun showVersion() { - with(loginFormVersion) { - visibility = VISIBLE - text = "${getString(R.string.app_name)} ${appInfo.versionName}" - } - } - - override fun showPrivacyPolicy() { - loginFormPrivacyLink.visibility = VISIBLE + loginFormVersion.text = "v${appInfo.versionName}" } override fun notifyParentAccountLogged(students: List, loginData: Triple) { @@ -156,6 +150,10 @@ class LoginFormFragment : BaseFragment(), LoginFormView { loginFormContact.visibility = if (show) VISIBLE else GONE } + override fun openAdvancedLogin() { + (activity as? LoginActivity)?.onAdvancedLoginClick() + } + override fun onDestroyView() { super.onDestroyView() presenter.onDetachView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt index 10c0a6636..0b45c9a53 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormPresenter.kt @@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.login.form import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.modules.login.LoginErrorHandler -import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.ifNullOrBlank @@ -14,8 +13,7 @@ class LoginFormPresenter @Inject constructor( schedulers: SchedulersProvider, studentRepository: StudentRepository, private val loginErrorHandler: LoginErrorHandler, - private val analytics: FirebaseAnalyticsHelper, - private val appInfo: AppInfo + private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(loginErrorHandler, studentRepository, schedulers) { override fun onAttachView(view: LoginFormView) { @@ -23,7 +21,7 @@ class LoginFormPresenter @Inject constructor( view.run { initView() showContact(false) - if (appInfo.isDebug) showVersion() else showPrivacyPolicy() + showVersion() loginErrorHandler.onBadCredentials = { setErrorPassIncorrect() @@ -37,6 +35,10 @@ class LoginFormPresenter @Inject constructor( view?.openPrivacyPolicyPage() } + fun onAdvancedLoginClick() { + view?.openAdvancedLogin() + } + fun onHostSelected() { view?.apply { clearPassError() @@ -56,13 +58,13 @@ class LoginFormPresenter @Inject constructor( } fun onSignInClick() { - val email = view?.formNameValue.orEmpty() - val password = view?.formPassValue.orEmpty() - val endpoint = view?.formHostValue.orEmpty() + val email = view?.formNameValue.orEmpty().trim() + val password = view?.formPassValue.orEmpty().trim() + val endpoint = view?.formHostValue.orEmpty().trim() if (!validateCredentials(email, password)) return - disposable.add(studentRepository.getStudents(email, password, endpoint) + disposable.add(studentRepository.getStudentsScrapper(email, password, endpoint) .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doOnSubscribe { @@ -81,11 +83,11 @@ class LoginFormPresenter @Inject constructor( } .subscribe({ Timber.i("Login result: Success") - analytics.logEvent("registration_form", "success" to true, "students" to it.size, "endpoint" to endpoint, "error" to "No error") + analytics.logEvent("registration_form", "success" to true, "students" to it.size, "scrapperBaseUrl" to endpoint, "error" to "No error") view?.notifyParentAccountLogged(it, Triple(email, password, endpoint)) }, { Timber.i("Login result: An exception occurred") - analytics.logEvent("registration_form", "success" to false, "students" to -1, "endpoint" to endpoint, "error" to it.message.ifNullOrBlank { "No message" }) + analytics.logEvent("registration_form", "success" to false, "students" to -1, "scrapperBaseUrl" to endpoint, "error" to it.message.ifNullOrBlank { "No message" }) loginErrorHandler.dispatch(it) view?.showContact(true) })) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt index 91e8d86ea..6da57c8c5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/form/LoginFormView.kt @@ -37,8 +37,6 @@ interface LoginFormView : BaseView { fun showVersion() - fun showPrivacyPolicy() - fun notifyParentAccountLogged(students: List, loginData: Triple) fun openPrivacyPolicyPage() @@ -48,4 +46,6 @@ interface LoginFormView : BaseView { fun openFaqPage() fun openEmail() + + fun openAdvancedLogin() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt index 288531dcc..841c225d7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectPresenter.kt @@ -79,11 +79,11 @@ class LoginStudentSelectPresenter @Inject constructor( Timber.i("Registration started") } .subscribe({ - students.forEach { analytics.logEvent("registration_student_select", "success" to true, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to "No error") } + students.forEach { analytics.logEvent("registration_student_select", "success" to true, "scrapperBaseUrl" to it.scrapperBaseUrl, "symbol" to it.symbol, "error" to "No error") } Timber.i("Registration result: Success") view?.openMainView() }, { error -> - students.forEach { analytics.logEvent("registration_student_select", "success" to false, "endpoint" to it.endpoint, "symbol" to it.symbol, "error" to error.message.ifNullOrBlank { "No message" }) } + students.forEach { analytics.logEvent("registration_student_select", "success" to false, "scrapperBaseUrl" to it.scrapperBaseUrl, "symbol" to it.symbol, "error" to error.message.ifNullOrBlank { "No message" }) } Timber.i("Registration result: An exception occurred ") loginErrorHandler.dispatch(error) view?.apply { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt index 49aabc926..ee6c30c3b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolPresenter.kt @@ -44,7 +44,7 @@ class LoginSymbolPresenter @Inject constructor( disposable.add( Single.fromCallable { if (loginData == null) throw IllegalArgumentException("Login data is null") else loginData } - .flatMap { studentRepository.getStudents(it.first, it.second, it.third, symbol) } + .flatMap { studentRepository.getStudentsScrapper(it.first, it.second, it.third, symbol) } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doOnSubscribe { @@ -62,7 +62,7 @@ class LoginSymbolPresenter @Inject constructor( } } .subscribe({ - analytics.logEvent("registration_symbol", "success" to true, "students" to it.size, "endpoint" to loginData?.third, "symbol" to symbol, "error" to "No error") + analytics.logEvent("registration_symbol", "success" to true, "students" to it.size, "scrapperBaseUrl" to loginData?.third, "symbol" to symbol, "error" to "No error") view?.apply { if (it.isEmpty()) { Timber.i("Login with symbol result: Empty student list") @@ -75,7 +75,7 @@ class LoginSymbolPresenter @Inject constructor( } }, { Timber.i("Login with symbol result: An exception occurred") - analytics.logEvent("registration_symbol", "success" to false, "students" to -1, "endpoint" to loginData?.third, "symbol" to symbol, "error" to it.message.ifNullOrBlank { "No message" }) + analytics.logEvent("registration_symbol", "success" to false, "students" to -1, "scrapperBaseUrl" to loginData?.third, "symbol" to symbol, "error" to it.message.ifNullOrBlank { "No message" }) loginErrorHandler.dispatch(it) view?.showContact(true) })) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt index 26568e22f..19d6ad1de 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageItem.kt @@ -9,6 +9,7 @@ import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.viewholders.FlexibleViewHolder import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.repositories.message.MessageFolder import io.github.wulkanowy.utils.toFormattedString import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.item_message.* @@ -27,7 +28,7 @@ class MessageItem(val message: Message, private val noSubjectString: String) : val style = if (message.unread) BOLD else NORMAL messageItemAuthor.run { - text = if (message.recipient.isNotBlank()) message.recipient else message.sender + text = if (message.folderId == MessageFolder.SENT.id) message.recipient else message.sender setTypeface(null, style) } messageItemSubject.run { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index c32bd6119..7b7404b86 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -1,6 +1,7 @@ package io.github.wulkanowy.ui.modules.message.preview import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.repositories.message.MessageFolder import io.github.wulkanowy.data.repositories.message.MessageRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter @@ -63,14 +64,14 @@ class MessagePreviewPresenter @Inject constructor( message.let { setSubject(if (it.subject.isNotBlank()) it.subject else noSubjectString) setDate(it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) - setContent(it.content.orEmpty()) + setContent(it.content) initOptions() - if (it.recipient.isNotBlank()) setRecipient(it.recipient) + if (it.folderId == MessageFolder.SENT.id) setRecipient(it.recipient) else setSender(it.sender) } } - analytics.logEvent("load_message_preview", "length" to message.content?.length) + analytics.logEvent("load_message_preview", "length" to message.content.length) }) { Timber.i("Loading message $id preview result: An exception occurred ") retryCallback = { onMessageLoadRetry() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index 1123e7e41..dc199207a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -4,6 +4,7 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.repositories.message.MessageFolder import io.github.wulkanowy.data.repositories.message.MessageRepository +import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler @@ -18,6 +19,7 @@ class MessageTabPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, + private val semesterRepository: SemesterRepository, private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler, studentRepository, schedulers) { @@ -76,8 +78,11 @@ class MessageTabPresenter @Inject constructor( disposable.apply { clear() add(studentRepository.getCurrentStudent() - .flatMap { messageRepository.getMessages(it, folder, forceRefresh) } - .map { items -> items.map { MessageItem(it, view?.noSubjectString.orEmpty()) } } + .flatMap { student -> + semesterRepository.getCurrentSemester(student) + .flatMap { messageRepository.getMessages(student, it, folder, forceRefresh) } + .map { items -> items.map { MessageItem(it, view?.noSubjectString.orEmpty()) } } + } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) .doFinally { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt index 7a9625c04..ef9c36fab 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreFragment.kt @@ -20,7 +20,6 @@ import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment 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.schoolandteachers.teacher.TeacherFragment import io.github.wulkanowy.utils.getCompatDrawable import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_more.* diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt index ed11607cd..1d043af3a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsErrorHandler.kt @@ -2,7 +2,7 @@ package io.github.wulkanowy.ui.modules.timetable.completed import android.content.res.Resources import com.readystatesoftware.chuck.api.ChuckCollector -import io.github.wulkanowy.api.interceptor.FeatureDisabledException +import io.github.wulkanowy.sdk.exception.FeatureDisabledException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt index 0030ee54a..62192a1b0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetProvider.kt @@ -28,7 +28,6 @@ import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.nextOrSameSchoolDay import io.github.wulkanowy.utils.nextSchoolDay import io.github.wulkanowy.utils.previousSchoolDay -import io.github.wulkanowy.utils.shortcutWeekDayName import io.github.wulkanowy.utils.toFormattedString import io.reactivex.Maybe import org.threeten.bp.LocalDate diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt index 22dbc22f5..c1e182ba2 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt @@ -32,7 +32,7 @@ fun Grade.getBackgroundColor(theme: String): Int { "B16CF1" -> R.color.grade_purple else -> R.color.grade_material_default } - "material" -> when (value) { + "material" -> when (value.toInt()) { 6 -> R.color.grade_material_six 5 -> R.color.grade_material_five 4 -> R.color.grade_material_four @@ -41,7 +41,7 @@ fun Grade.getBackgroundColor(theme: String): Int { 1 -> R.color.grade_material_one else -> R.color.grade_material_default } - else -> when (value) { + else -> when (value.toInt()) { 6 -> R.color.grade_vulcan_six 5 -> R.color.grade_vulcan_five 4 -> R.color.grade_vulcan_four diff --git a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt index 24e6d3ffa..264f45426 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/security/Scrambler.kt @@ -14,7 +14,6 @@ import android.security.keystore.KeyProperties.DIGEST_SHA512 import android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_OAEP import android.security.keystore.KeyProperties.PURPOSE_DECRYPT import android.security.keystore.KeyProperties.PURPOSE_ENCRYPT -import android.util.Base64 import android.util.Base64.DEFAULT import android.util.Base64.decode import android.util.Base64.encode @@ -60,7 +59,7 @@ fun encrypt(plainText: String, context: Context): String { if (plainText.isEmpty()) throw ScramblerException("Text to be encrypted is empty") if (SDK_INT < JELLY_BEAN_MR2) { - return String(Base64.encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET) + return String(encode(plainText.toByteArray(KEY_CHARSET), DEFAULT), KEY_CHARSET) } return try { diff --git a/app/src/main/res/layout/fragment_login_advanced.xml b/app/src/main/res/layout/fragment_login_advanced.xml new file mode 100644 index 000000000..094e37d15 --- /dev/null +++ b/app/src/main/res/layout/fragment_login_advanced.xml @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_login_form.xml b/app/src/main/res/layout/fragment_login_form.xml index 9f7f01e0f..bb93c1827 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -24,7 +24,8 @@ + android:layout_height="wrap_content" + android:paddingBottom="16dp"> + + + + - - diff --git a/app/src/main/res/layout/fragment_login_symbol.xml b/app/src/main/res/layout/fragment_login_symbol.xml index 0f6916496..56b4a32e5 100644 --- a/app/src/main/res/layout/fragment_login_symbol.xml +++ b/app/src/main/res/layout/fragment_login_symbol.xml @@ -141,7 +141,7 @@ android:layout_height="wrap_content" android:imeActionLabel="@string/login_sign_in" android:imeOptions="actionDone" - android:inputType="textAutoComplete" + android:inputType="textAutoComplete|textNoSuggestions" android:maxLines="1" tools:ignore="LabelFor" /> diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 9026fa0c1..a88a42a79 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -27,6 +27,12 @@ Hasło Dziennik Symbol + Mobilne API + Scrapper + Hybrydowe + Token + PIN + Klucz API Zaloguj To hasło jest za krótkie Dane logowania są niepoprawne @@ -35,6 +41,7 @@ Wybrany uczeń jest już zalogowany Symbol znajdziesz na stronie dziennika w Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne Wybierz uczniów do zalogowania w aplikacji + Inne opcje Polityka prywatności Problemy z logowaniem? Napisz do nas! Email @@ -353,4 +360,5 @@ Dziennik jest niedostępny. Spróbuj ponownie później Wystąpił nieoczekiwany błąd Funkcja wyłączona przez szkołę + Funkcja niedostępna w tym trybie diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e056e35ac..439bf19d6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,6 +26,12 @@ Email or nick Password Register + Mobile API + Scrapper + Hybrid + Token + PIN + API key Symbol Sign in This password is too short @@ -35,6 +41,7 @@ The selected student is already logged in The symbol can be found on the register page in Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne Select students to log in to the application + Other options Privacy policy Trouble signing in? Contact us! Email @@ -334,4 +341,5 @@ The log is not available. Try again later An unexpected error occurred Feature disabled by your school + Feature not available in this mode diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemoteTest.kt index 8adfa83bd..318d4ff76 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemoteTest.kt @@ -1,23 +1,22 @@ package io.github.wulkanowy.data.repositories.attendance -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.attendance.Attendance import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.Attendance import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.SpyK import io.reactivex.Single import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.threeten.bp.LocalDate -import java.sql.Date +import org.threeten.bp.LocalDate.of class AttendanceRemoteTest { - @SpyK - private var mockApi = Api() + @MockK + private lateinit var mockSdk: Sdk @MockK private lateinit var semesterMock: Semester @@ -29,29 +28,44 @@ class AttendanceRemoteTest { @Test fun getAttendanceTest() { - every { mockApi.getAttendance( - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 15) - ) } returns Single.just(listOf( - getAttendance("2018-09-10"), - getAttendance("2018-09-17") + every { + mockSdk.getAttendance( + of(2018, 9, 10), + of(2018, 9, 15), + 1 + ) + } returns Single.just(listOf( + getAttendance(of(2018, 9, 10)), + getAttendance(of(2018, 9, 17)) )) - every { mockApi.diaryId } returns 1 every { semesterMock.studentId } returns 1 every { semesterMock.diaryId } returns 1 + every { semesterMock.schoolYear } returns 2019 + every { semesterMock.semesterId } returns 1 + every { mockSdk.switchDiary(any(), any()) } returns mockSdk - val attendance = AttendanceRemote(mockApi).getAttendance(semesterMock, - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 15)).blockingGet() + val attendance = AttendanceRemote(mockSdk).getAttendance(semesterMock, + of(2018, 9, 10), + of(2018, 9, 15) + ).blockingGet() assertEquals(2, attendance.size) } - private fun getAttendance(dateString: String): Attendance { - return Attendance().apply { - subject = "Fizyka" - name = "Obecność" - date = Date.valueOf(dateString) - } + private fun getAttendance(date: LocalDate): Attendance { + return Attendance( + subject = "Fizyka", + name = "Obecność", + date = date, + number = 0, + deleted = false, + excusable = false, + excused = false, + exemption = false, + lateness = false, + presence = false, + categoryId = 1, + absence = false + ) } } diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemoteTest.kt index bb9f52945..7570164fd 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/completedlessons/CompletedLessonsRemoteTest.kt @@ -1,23 +1,22 @@ package io.github.wulkanowy.data.repositories.completedlessons -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.timetable.CompletedLesson import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.CompletedLesson import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.SpyK import io.reactivex.Single import org.junit.Assert import org.junit.Before import org.junit.Test import org.threeten.bp.LocalDate -import java.sql.Date +import org.threeten.bp.LocalDate.of class CompletedLessonsRemoteTest { - @SpyK - private var mockApi = Api() + @MockK + private lateinit var mockSdk: Sdk @MockK private lateinit var semesterMock: Semester @@ -30,27 +29,39 @@ class CompletedLessonsRemoteTest { @Test fun getCompletedLessonsTest() { every { - mockApi.getCompletedLessons( - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 15) + mockSdk.getCompletedLessons( + of(2018, 9, 10), + of(2018, 9, 15) ) } returns Single.just(listOf( - getCompletedLesson("2018-09-10"), - getCompletedLesson("2018-09-17") + getCompletedLesson(of(2018, 9, 10)), + getCompletedLesson(of(2018, 9, 17)) )) - every { mockApi.diaryId } returns 1 every { semesterMock.studentId } returns 1 every { semesterMock.diaryId } returns 1 + every { semesterMock.schoolYear } returns 2019 + every { semesterMock.semesterId } returns 1 + every { mockSdk.switchDiary(any(), any()) } returns mockSdk - val completed = CompletedLessonsRemote(mockApi).getCompletedLessons(semesterMock, - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 15) + val completed = CompletedLessonsRemote(mockSdk).getCompletedLessons(semesterMock, + of(2018, 9, 10), + of(2018, 9, 15) ).blockingGet() Assert.assertEquals(2, completed.size) } - private fun getCompletedLesson(dateString: String): CompletedLesson { - return CompletedLesson().apply { date = Date.valueOf(dateString) } + private fun getCompletedLesson(date: LocalDate): CompletedLesson { + return CompletedLesson( + date = date, + subject = "", + absence = "", + resources = "", + substitution = "", + teacherSymbol = "", + teacher = "", + topic = "", + number = 1 + ) } } diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/exam/ExamRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/exam/ExamRemoteTest.kt index 0c83bed0a..c17ec21eb 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/exam/ExamRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/exam/ExamRemoteTest.kt @@ -1,23 +1,22 @@ package io.github.wulkanowy.data.repositories.exam -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.exams.Exam import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.Exam import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.SpyK import io.reactivex.Single import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.threeten.bp.LocalDate -import java.sql.Date +import org.threeten.bp.LocalDate.of class ExamRemoteTest { - @SpyK - private var mockApi = Api() + @MockK + private lateinit var mockSdk: Sdk @MockK private lateinit var semesterMock: Semester @@ -29,35 +28,41 @@ class ExamRemoteTest { @Test fun getExamsTest() { - every { mockApi.getExams( - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 15) - ) } returns Single.just(listOf( - getExam("2018-09-10"), - getExam("2018-09-17") + every { + mockSdk.getExams( + of(2018, 9, 10), + of(2018, 9, 15), + 1 + ) + } returns Single.just(listOf( + getExam(of(2018, 9, 10)), + getExam(of(2018, 9, 17)) )) - every { mockApi.diaryId } returns 1 every { semesterMock.studentId } returns 1 every { semesterMock.diaryId } returns 1 + every { semesterMock.schoolYear } returns 2019 + every { semesterMock.semesterId } returns 1 + every { mockSdk.switchDiary(any(), any()) } returns mockSdk - val exams = ExamRemote(mockApi).getExams(semesterMock, - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 15) - ).blockingGet() + val exams = ExamRemote(mockSdk) + .getExams(semesterMock, + of(2018, 9, 10), + of(2018, 9, 15) + ).blockingGet() assertEquals(2, exams.size) } - private fun getExam(dateString: String): Exam { - return Exam().apply { - subject = "" - group = "" - type = "" - description = "" - teacher = "" - teacherSymbol = "" - date = Date.valueOf(dateString) - entryDate = Date.valueOf(dateString) - } + private fun getExam(date: LocalDate): Exam { + return Exam( + subject = "", + group = "", + type = "", + description = "", + teacher = "", + teacherSymbol = "", + date = date, + entryDate = date + ) } } diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemoteTest.kt index 2fb566e7c..ee08454f1 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemoteTest.kt @@ -1,13 +1,12 @@ package io.github.wulkanowy.data.repositories.gradestatistics -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.grades.GradePointsSummary -import io.github.wulkanowy.api.grades.GradeStatistics import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.GradePointsStatistics +import io.github.wulkanowy.sdk.pojo.GradeStatistics import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.SpyK import io.reactivex.Single import org.junit.Assert.assertEquals import org.junit.Before @@ -15,8 +14,8 @@ import org.junit.Test class GradeStatisticsRemoteTest { - @SpyK - private var mockApi = Api() + @MockK + private lateinit var mockSdk: Sdk @MockK private lateinit var semesterMock: Semester @@ -28,48 +27,51 @@ class GradeStatisticsRemoteTest { @Test fun getGradeStatisticsTest() { - every { mockApi.getGradesPartialStatistics(1) } returns Single.just(listOf( + every { mockSdk.getGradesPartialStatistics(1) } returns Single.just(listOf( getGradeStatistics("Fizyka"), getGradeStatistics("Matematyka") )) - every { mockApi.diaryId } returns 1 every { semesterMock.studentId } returns 1 - every { semesterMock.semesterId } returns 1 - every { semesterMock.semesterName } returns 2 every { semesterMock.diaryId } returns 1 + every { semesterMock.schoolYear } returns 2019 + every { semesterMock.semesterId } returns 1 + every { mockSdk.switchDiary(any(), any()) } returns mockSdk - val stats = GradeStatisticsRemote(mockApi).getGradeStatistics(semesterMock, false).blockingGet() + val stats = GradeStatisticsRemote(mockSdk).getGradeStatistics(semesterMock, false).blockingGet() assertEquals(2, stats.size) } @Test fun getGradePointsStatisticsTest() { - every { mockApi.getGradesPointsStatistics(1) } returns Single.just(listOf( + every { mockSdk.getGradesPointsStatistics(1) } returns Single.just(listOf( getGradePointsStatistics("Fizyka"), getGradePointsStatistics("Matematyka") )) - every { mockApi.diaryId } returns 1 every { semesterMock.studentId } returns 1 - every { semesterMock.semesterId } returns 1 - every { semesterMock.semesterName } returns 2 every { semesterMock.diaryId } returns 1 + every { semesterMock.schoolYear } returns 2019 + every { semesterMock.semesterId } returns 1 + every { mockSdk.switchDiary(any(), any()) } returns mockSdk - val stats = GradeStatisticsRemote(mockApi).getGradePointsStatistics(semesterMock).blockingGet() + val stats = GradeStatisticsRemote(mockSdk).getGradePointsStatistics(semesterMock).blockingGet() assertEquals(2, stats.size) } private fun getGradeStatistics(subjectName: String): GradeStatistics { - return GradeStatistics().apply { - subject = subjectName - gradeValue = 5 - amount = 10 - } + return GradeStatistics( + subject = subjectName, + gradeValue = 5, + amount = 10, + grade = "", + semesterId = 1 + ) } - private fun getGradePointsStatistics(subjectName: String): GradePointsSummary { - return GradePointsSummary( + private fun getGradePointsStatistics(subjectName: String): GradePointsStatistics { + return GradePointsStatistics( + semesterId = 1, subject = subjectName, student = 0.80, others = 0.40 diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemoteTest.kt index 67e6c2ef6..e8699f1ee 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/luckynumber/LuckyNumberRemoteTest.kt @@ -1,8 +1,7 @@ package io.github.wulkanowy.data.repositories.luckynumber -import io.github.wulkanowy.api.Api import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRemote +import io.github.wulkanowy.sdk.Sdk import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK @@ -16,7 +15,7 @@ import org.threeten.bp.LocalDate class LuckyNumberRemoteTest { @SpyK - private var mockApi = Api() + private var mockSdk = Sdk() @MockK private lateinit var semesterMock: Semester @@ -28,13 +27,13 @@ class LuckyNumberRemoteTest { @Test fun getLuckyNumberTest() { - every { mockApi.getLuckyNumber() } returns Maybe.just(14) + every { mockSdk.getLuckyNumber() } returns Maybe.just(14) - every { mockApi.diaryId } returns 1 + every { mockSdk.diaryId } returns 1 every { semesterMock.studentId } returns 1 every { semesterMock.diaryId } returns 1 - val luckyNumber = LuckyNumberRemote(mockApi) + val luckyNumber = LuckyNumberRemote(mockSdk) .getLuckyNumber(semesterMock) .blockingGet() diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/semester/SemesterRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/semester/SemesterRepositoryTest.kt index 7856b9370..808354761 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/semester/SemesterRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/semester/SemesterRepositoryTest.kt @@ -1,7 +1,7 @@ package io.github.wulkanowy.data.repositories.semester import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.ApiHelper +import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.UnitTestInternetObservingStrategy @@ -24,7 +24,7 @@ class SemesterRepositoryTest { private lateinit var semesterLocal: SemesterLocal @Mock - private lateinit var apiHelper: ApiHelper + private lateinit var sdkHelper: SdkHelper @Mock private lateinit var student: Student @@ -38,7 +38,7 @@ class SemesterRepositoryTest { @Before fun initTest() { MockitoAnnotations.initMocks(this) - semesterRepository = SemesterRepository(semesterRemote, semesterLocal, settings, apiHelper) + semesterRepository = SemesterRepository(semesterRemote, semesterLocal, settings, sdkHelper) } @Test @@ -48,7 +48,7 @@ class SemesterRepositoryTest { createSemesterEntity(true) ) - doNothing().`when`(apiHelper).initApi(student) + doNothing().`when`(sdkHelper).init(student) doReturn(Maybe.empty()).`when`(semesterLocal).getSemesters(student) doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student) @@ -65,7 +65,7 @@ class SemesterRepositoryTest { createSemesterEntity(true) ) - doNothing().`when`(apiHelper).initApi(student) + doNothing().`when`(sdkHelper).init(student) doReturn(Maybe.empty()).`when`(semesterLocal).getSemesters(student) doReturn(Single.just(semesters)).`when`(semesterRemote).getSemesters(student) diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt index 4d7b23ebd..f52c01424 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/student/StudentRemoteTest.kt @@ -1,19 +1,23 @@ package io.github.wulkanowy.data.repositories.student -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.register.Student +import io.github.wulkanowy.data.SdkHelper +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.Student +import io.mockk.impl.annotations.SpyK import io.reactivex.Single import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.mockito.Mock +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.anyString import org.mockito.Mockito.doReturn import org.mockito.MockitoAnnotations class StudentRemoteTest { @Mock - private lateinit var mockApi: Api + private lateinit var mockSdk: Sdk @Before fun initApi() { @@ -22,11 +26,31 @@ class StudentRemoteTest { @Test fun testRemoteAll() { - doReturn(Single.just(listOf(Student("", "", 1, "test", "", "", "", 1, Api.LoginType.AUTO)))) - .`when`(mockApi).getStudents() + doReturn(Single.just(listOf(getStudent("test")))).`when`(mockSdk).getStudentsFromScrapper(anyString(), anyString(), anyString(), anyString()) - val students = StudentRemote(mockApi).getStudents("", "", "").blockingGet() + val students = StudentRemote(mockSdk).getStudentsScrapper("", "", "http://fakelog.cf", "").blockingGet() assertEquals(1, students.size) assertEquals("test", students.first().studentName) } + + private fun getStudent(name: String): Student { + return Student( + email = "", + symbol = "", + studentId = 0, + userLoginId = 0, + studentName = name, + schoolSymbol = "", + schoolName = "", + className = "", + classId = 0, + certificateKey = "", + privateKey = "", + loginMode = Sdk.Mode.SCRAPPER, + mobileBaseUrl = "", + loginType = Sdk.ScrapperLoginType.STANDARD, + scrapperBaseUrl = "", + isParent = false + ) + } } diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemoteTest.kt index 4bb4091fb..100bc0aed 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemoteTest.kt @@ -1,24 +1,23 @@ package io.github.wulkanowy.data.repositories.timetable -import io.github.wulkanowy.api.Api -import io.github.wulkanowy.api.timetable.Timetable import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.repositories.timetable.TimetableRemote +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.Timetable import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.SpyK import io.reactivex.Single import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.threeten.bp.LocalDate -import java.sql.Date +import org.threeten.bp.LocalDate.of +import org.threeten.bp.LocalDateTime.now class TimetableRemoteTest { - @SpyK - private var mockApi = Api() + @MockK + private lateinit var mockSdk: Sdk @MockK private lateinit var semesterMock: Semester @@ -30,26 +29,46 @@ class TimetableRemoteTest { @Test fun getTimetableTest() { - every { mockApi.getTimetable( - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 15) - ) } returns Single.just(listOf( - getTimetable("2018-09-10"), - getTimetable("2018-09-17") + every { + mockSdk.getTimetable( + of(2018, 9, 10), + of(2018, 9, 15) + ) + } returns Single.just(listOf( + getTimetable(of(2018, 9, 10)), + getTimetable(of(2018, 9, 17)) )) - every { mockApi.diaryId } returns 1 every { semesterMock.studentId } returns 1 every { semesterMock.diaryId } returns 1 + every { semesterMock.schoolYear } returns 2019 + every { semesterMock.semesterId } returns 1 + every { mockSdk.switchDiary(any(), any()) } returns mockSdk - val timetable = TimetableRemote(mockApi).getTimetable(semesterMock, - LocalDate.of(2018, 9, 10), - LocalDate.of(2018, 9, 15) + val timetable = TimetableRemote(mockSdk).getTimetable(semesterMock, + of(2018, 9, 10), + of(2018, 9, 15) ).blockingGet() assertEquals(2, timetable.size) } - private fun getTimetable(dateString: String): Timetable { - return Timetable(date = Date.valueOf(dateString)) + private fun getTimetable(date: LocalDate): Timetable { + return Timetable( + date = date, + number = 0, + teacherOld = "", + subjectOld = "", + roomOld = "", + subject = "", + teacher = "", + group = "", + canceled = false, + changes = false, + info = "", + room = "", + end = now(), + start = now(), + studentPlan = true + ) } } 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 3197bb794..56b1e66f8 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 @@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.grade.GradeRepository import io.github.wulkanowy.data.repositories.gradessummary.GradeSummaryRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository +import io.github.wulkanowy.sdk.Sdk import io.reactivex.Single import org.junit.Assert.assertEquals import org.junit.Before @@ -30,7 +31,7 @@ class GradeAverageProviderTest { private lateinit var gradeAverageProvider: GradeAverageProvider - private val student = Student("", "", "", "", "", 101, "", "", "", "", 1, true, LocalDateTime.now()) + private val student = Student("", "", "", "SCRAPPER", "", "", false, "", "", "", 101, 0, "", "", "", "", 1, true, LocalDateTime.now()) private val semesters = mutableListOf( Semester(101, 10, "", 1, 21, 1, false, now(), now(), 1, 1), @@ -39,17 +40,22 @@ class GradeAverageProviderTest { ) private val firstGrades = listOf( - getGrade(22, "Matematyka", 4), - getGrade(22, "Matematyka", 3), - getGrade(22, "Fizyka", 6), - getGrade(22, "Fizyka", 1) + getGrade(22, "Matematyka", 4.0), + getGrade(22, "Matematyka", 3.0), + getGrade(22, "Fizyka", 6.0), + getGrade(22, "Fizyka", 1.0) ) private val secondGrade = listOf( - getGrade(23, "Matematyka", 2), - getGrade(23, "Matematyka", 3), - getGrade(23, "Fizyka", 4), - getGrade(23, "Fizyka", 2) + getGrade(23, "Matematyka", 2.0), + getGrade(23, "Matematyka", 3.0), + getGrade(23, "Fizyka", 4.0), + getGrade(23, "Fizyka", 2.0) + ) + + private val secondGradeWithModifier = listOf( + getGrade(24, "Język polski", 3.0, -0.50), + getGrade(24, "Język polski", 4.0, 0.25) ) @Before @@ -78,6 +84,54 @@ class GradeAverageProviderTest { assertEquals(3.0, averages["Fizyka"]) } + @Test + fun onlyOneSemester_gradesWithModifiers_default() { + doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode + doReturn(Single.just(emptyList())).`when`(gradeSummaryRepository).getGradesSummary(semesters[2], true) + doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student, semesters[2], true) + + val averages = gradeAverageProvider.getGradeAverage(student, semesters, semesters[2].semesterId, true) + .blockingGet() + + assertEquals(3.5, averages["Język polski"]) + } + + @Test + fun onlyOneSemester_gradesWithModifiers_api() { + doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode + doReturn(Single.just(emptyList())).`when`(gradeSummaryRepository).getGradesSummary(semesters[2], true) + doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student.copy(loginMode = Sdk.Mode.API.name), semesters[2], true) + + val averages = gradeAverageProvider.getGradeAverage(student.copy(loginMode = Sdk.Mode.API.name), semesters, semesters[2].semesterId, true) + .blockingGet() + + assertEquals(3.375, averages["Język polski"]) + } + + @Test + fun onlyOneSemester_gradesWithModifiers_scrapper() { + doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode + doReturn(Single.just(emptyList())).`when`(gradeSummaryRepository).getGradesSummary(semesters[2], true) + doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student.copy(loginMode = Sdk.Mode.SCRAPPER.name), semesters[2], true) + + val averages = gradeAverageProvider.getGradeAverage(student.copy(loginMode = Sdk.Mode.SCRAPPER.name), semesters, semesters[2].semesterId, true) + .blockingGet() + + assertEquals(3.5, averages["Język polski"]) + } + + @Test + fun onlyOneSemester_gradesWithModifiers_hybrid() { + doReturn("only_one_semester").`when`(preferencesRepository).gradeAverageMode + doReturn(Single.just(emptyList())).`when`(gradeSummaryRepository).getGradesSummary(semesters[2], true) + doReturn(Single.just(secondGradeWithModifier)).`when`(gradeRepository).getGrades(student.copy(loginMode = Sdk.Mode.HYBRID.name), semesters[2], true) + + val averages = gradeAverageProvider.getGradeAverage(student.copy(loginMode = Sdk.Mode.HYBRID.name), semesters, semesters[2].semesterId, true) + .blockingGet() + + assertEquals(3.375, averages["Język polski"]) + } + @Test fun allYearFirstSemesterTest() { doReturn("all_year").`when`(preferencesRepository).gradeAverageMode @@ -147,13 +201,13 @@ class GradeAverageProviderTest { assertEquals(3.25, averages["Fizyka"]) } - private fun getGrade(semesterId: Int, subject: String, value: Int): Grade { + private fun getGrade(semesterId: Int, subject: String, value: Double, modifier: Double = 0.0): Grade { return Grade( studentId = 101, semesterId = semesterId, subject = subject, value = value, - modifier = .0, + modifier = modifier, weightValue = 1.0, teacher = "", date = now(), 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 453adfba0..be99d4d46 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 @@ -43,7 +43,7 @@ class LoginFormPresenterTest { fun initPresenter() { MockitoAnnotations.initMocks(this) clearInvocations(repository, loginFormView) - presenter = LoginFormPresenter(TestSchedulersProvider(), repository, errorHandler, analytics, appInfo) + presenter = LoginFormPresenter(TestSchedulersProvider(), repository, errorHandler, analytics) presenter.onAttachView(loginFormView) } @@ -90,9 +90,8 @@ class LoginFormPresenterTest { @Test fun loginTest() { - val studentTest = Student(email = "test@", password = "123", endpoint = "https://fakelog.cf", loginType = "AUTO", studentName = "", schoolSymbol = "", schoolName = "", studentId = 0, classId = 1, isCurrent = false, symbol = "", registrationDate = now(), className = "") - doReturn(Single.just(listOf(studentTest))) - .`when`(repository).getStudents(anyString(), anyString(), anyString(), anyString()) + 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, isParent = false) + doReturn(Single.just(listOf(studentTest))).`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") @@ -109,7 +108,7 @@ class LoginFormPresenterTest { @Test fun loginEmptyTest() { doReturn(Single.just(emptyList())) - .`when`(repository).getStudents(anyString(), anyString(), anyString(), anyString()) + .`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") @@ -125,7 +124,7 @@ class LoginFormPresenterTest { @Test fun loginEmptyTwiceTest() { doReturn(Single.just(emptyList())) - .`when`(repository).getStudents(anyString(), anyString(), anyString(), anyString()) + .`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") @@ -142,8 +141,7 @@ class LoginFormPresenterTest { @Test fun loginErrorTest() { val testException = RuntimeException("test") - doReturn(Single.error>(testException)) - .`when`(repository).getStudents(anyString(), anyString(), anyString(), anyString()) + doReturn(Single.error>(testException)).`when`(repository).getStudentsScrapper(anyString(), anyString(), anyString(), anyString()) `when`(loginFormView.formNameValue).thenReturn("@") `when`(loginFormView.formPassValue).thenReturn("123456") `when`(loginFormView.formHostValue).thenReturn("https://fakelog.cf") @@ -157,4 +155,3 @@ class LoginFormPresenterTest { verify(errorHandler).dispatch(testException) } } - 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 16bcdebe8..2c174fb58 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 @@ -32,7 +32,7 @@ class LoginStudentSelectPresenterTest { private lateinit var presenter: LoginStudentSelectPresenter - private val testStudent by lazy { Student(email = "test", password = "test123", endpoint = "https://fakelog.cf", loginType = "AUTO", symbol = "", isCurrent = false, studentId = 0, schoolName = "", schoolSymbol = "", classId = 1, studentName = "", registrationDate = now(), className = "") } + 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 = "", userLoginId = 1, isParent = false) } private val testException by lazy { RuntimeException("Problem") } diff --git a/app/src/test/java/io/github/wulkanowy/utils/GradeExtensionTest.kt b/app/src/test/java/io/github/wulkanowy/utils/GradeExtensionTest.kt index 2bba619f0..ece7acf20 100644 --- a/app/src/test/java/io/github/wulkanowy/utils/GradeExtensionTest.kt +++ b/app/src/test/java/io/github/wulkanowy/utils/GradeExtensionTest.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.utils +import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.GradeSummary import org.junit.Assert.assertEquals @@ -22,11 +23,11 @@ class GradeExtensionTest { @Test fun calcWeightedAverage() { assertEquals(3.47, listOf( - createGrade(5, 6.0, 0.33), - createGrade(5, 5.0, -0.33), - createGrade(4, 1.0, 0.0), - createGrade(1, 9.0, 0.5), - createGrade(0, .0, 0.0) + createGrade(5.0, 6.0, 0.33), + createGrade(5.0, 5.0, -0.33), + createGrade(4.0, 1.0, 0.0), + createGrade(1.0, 9.0, 0.5), + createGrade(0.0, .0, 0.0) ).calcAverage(), 0.005) } @@ -40,25 +41,33 @@ class GradeExtensionTest { ).calcAverage(), 0.005) } + @Test + fun getBackgroundColor() { + assertEquals(R.color.grade_material_five, createGrade(5.0).getBackgroundColor("material")) + assertEquals(R.color.grade_material_five, createGrade(5.5).getBackgroundColor("material")) + assertEquals(R.color.grade_material_five, createGrade(5.9).getBackgroundColor("material")) + assertEquals(R.color.grade_vulcan_five, createGrade(5.9).getBackgroundColor("whatever")) + } + @Test fun changeModifier_zero() { - assertEquals(.0, createGrade(5, .0, .5).changeModifier(.0, .0).modifier, .0) - assertEquals(.0, createGrade(5, .0, -.5).changeModifier(.0, .0).modifier, .0) + assertEquals(.0, createGrade(5.0, .0, .5).changeModifier(.0, .0).modifier, .0) + assertEquals(.0, createGrade(5.0, .0, -.5).changeModifier(.0, .0).modifier, .0) } @Test fun changeModifier_plus() { - assertEquals(.33, createGrade(5, .0, .25).changeModifier(.33, .50).modifier, .0) - assertEquals(.25, createGrade(5, .0, .33).changeModifier(.25, .0).modifier, .0) + assertEquals(.33, createGrade(5.0, .0, .25).changeModifier(.33, .50).modifier, .0) + assertEquals(.25, createGrade(5.0, .0, .33).changeModifier(.25, .0).modifier, .0) } @Test fun changeModifier_minus() { - assertEquals(-.33, createGrade(5, .0, -.25).changeModifier(.25, .33).modifier, .0) - assertEquals(-.25, createGrade(5, .0, -.33).changeModifier(.0, .25).modifier, .0) + assertEquals(-.33, createGrade(5.0, .0, -.25).changeModifier(.25, .33).modifier, .0) + assertEquals(-.25, createGrade(5.0, .0, -.33).changeModifier(.0, .25).modifier, .0) } - private fun createGrade(value: Int, weightValue: Double = .0, modifier: Double = 0.25): Grade { + private fun createGrade(value: Double, weightValue: Double = .0, modifier: Double = 0.25): Grade { return Grade( semesterId = 1, studentId = 1, From 2e7d2b66f8d9ec5d7f66017ffda5af080ce83ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 22 Dec 2019 20:18:48 +0100 Subject: [PATCH 012/236] Fix lucky number empty screen (#623) --- .../wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt index 63ced4b89..c86aab976 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/LuckyNumberPresenter.kt @@ -66,7 +66,7 @@ class LuckyNumberPresenter @Inject constructor( view?.run { showContent(false) showEmpty(true) - showEmpty(false) + showErrorView(false) } }) ) From 9d27723f30a3582bc50668f6d51eadb06921c257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 22 Dec 2019 20:19:31 +0100 Subject: [PATCH 013/236] =?UTF-8?q?Don't=20copy=20teacher=20from=20previou?= =?UTF-8?q?s=20lesson=20with=20changes=20if=20new=20one=E2=80=A6=20(#622)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../timetable/TimetableRepositoryTest.kt | 63 +++++++++++++------ .../timetable/TimetableRepository.kt | 2 +- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt index 073f822a2..565425d43 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepositoryTest.kt @@ -6,7 +6,6 @@ import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings -import io.github.wulkanowy.data.SdkHelper import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy @@ -14,9 +13,6 @@ import io.github.wulkanowy.sdk.Sdk import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.impl.annotations.SpyK -import io.mockk.just -import io.mockk.runs import io.reactivex.Single import org.junit.After import org.junit.Before @@ -94,27 +90,58 @@ class TimetableRepositoryTest { @Test fun copyTeacherToCompletedFromPrevious() { timetableLocal.saveTimetable(listOf( - createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "123", "Przyroda", "Jan Garnkiewicz", false), - createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "321", "Religia", "Paweł Jumper", false), - createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "213", "W-F", "", true), - createTimetableLocal(of(2019, 3, 5, 10, 30), 4, "213", "W-F", "", false) + createTimetableLocal(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false), + createTimetableLocal(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Paweł Poniedziałkowski", false), + createTimetableLocal(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Wtorkowska", true), + createTimetableLocal(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true), + + createTimetableLocal(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "Joanna Wtorkowska", false), + createTimetableLocal(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "Joanna Wtorkowska", false), + createTimetableLocal(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "Joanna Środowska", true), + createTimetableLocal(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "Joanna Środowska", true), + + createTimetableLocal(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "", false), + createTimetableLocal(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "", false), + createTimetableLocal(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "", true), + createTimetableLocal(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "", true) )) every { mockSdk.getTimetable(any(), any()) } returns Single.just(listOf( - createTimetableRemote(of(2019, 3, 5, 8, 0), 1, "", "Przyroda", "", true), // should override local - createTimetableRemote(of(2019, 3, 5, 8, 50), 2, "", "Religia", "", false), - createTimetableRemote(of(2019, 3, 5, 9, 40), 3, "", "W-F", "Jan Garnkiewicz", false), - createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F", "Paweł Jumper", false) + createTimetableRemote(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false), + createTimetableRemote(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Jakub Wtorkowski", true), + createTimetableRemote(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Poniedziałkowska", false), + createTimetableRemote(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true), + + createTimetableRemote(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "", false), + createTimetableRemote(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "", true), + createTimetableRemote(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "", false), + createTimetableRemote(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "", true), + + createTimetableRemote(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "Paweł Środowski", false), + createTimetableRemote(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "Paweł Czwartkowski", true), + createTimetableRemote(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "Paweł Środowski", false), + createTimetableRemote(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true) )) val lessons = TimetableRepository(settings, timetableLocal, timetableRemote) - .getTimetable(semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true) + .getTimetable(semesterMock, LocalDate.of(2019, 12, 23), LocalDate.of(2019, 12, 25), true) .blockingGet() - assertEquals(4, lessons.size) - assertEquals("", lessons[0].teacher) - assertEquals("Paweł Jumper", lessons[1].teacher) - assertEquals("Jan Garnkiewicz", lessons[2].teacher) - assertEquals("Paweł Jumper", lessons[3].teacher) + assertEquals(12, lessons.size) + + assertEquals("Paweł Poniedziałkowski", lessons[0].teacher) + assertEquals("Jakub Wtorkowski", lessons[1].teacher) + assertEquals("Joanna Poniedziałkowska", lessons[2].teacher) + assertEquals("Joanna Wtorkowska", lessons[3].teacher) + + assertEquals("Joanna Wtorkowska", lessons[4].teacher) + assertEquals("", lessons[5].teacher) + assertEquals("", lessons[6].teacher) + assertEquals("", lessons[7].teacher) + + assertEquals("Paweł Środowski", lessons[8].teacher) + assertEquals("Paweł Czwartkowski", lessons[9].teacher) + assertEquals("Paweł Środowski", lessons[10].teacher) + assertEquals("Paweł Czwartkowski", lessons[11].teacher) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt index e10e958ec..20e183c25 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRepository.kt @@ -36,7 +36,7 @@ class TimetableRepository @Inject constructor( old.singleOrNull { new.start == it.start }?.let { old -> return@map new.copy( room = if (new.room.isEmpty()) old.room else new.room, - teacher = if (new.teacher.isEmpty() && !new.changes) old.teacher else new.teacher + teacher = if (new.teacher.isEmpty() && !new.changes && !old.changes) old.teacher else new.teacher ) } } From 65995cdc6c33e2082d781088a76544df974747c3 Mon Sep 17 00:00:00 2001 From: Nikolas Ostrowski <28026071+nixls@users.noreply.github.com> Date: Tue, 24 Dec 2019 16:51:35 +0100 Subject: [PATCH 014/236] Change Wulkanowy API link to SDK in README (#626) --- README.en.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.en.md b/README.en.md index 29da5586e..4da00e5dd 100644 --- a/README.en.md +++ b/README.en.md @@ -46,7 +46,7 @@ You can also download a [development version](https://wulkanowy.github.io/#downl ## Built With -* [Wulkanowy API](https://github.com/wulkanowy/api) +* [Wulkanowy SDK](https://github.com/wulkanowy/sdk) * [RxJava 2](https://github.com/ReactiveX/RxJava) * [Dagger 2](https://github.com/google/dagger) * [Room](https://developer.android.com/topic/libraries/architecture/room) diff --git a/README.md b/README.md index 59b74b3eb..d51115d5e 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#downloa ## Zbudowana za pomocą -* [Wulkanowy API](https://github.com/wulkanowy/api) +* [Wulkanowy SDK](https://github.com/wulkanowy/SDK) * [RxJava 2](https://github.com/ReactiveX/RxJava) * [Dagger 2](https://github.com/google/dagger) * [Room](https://developer.android.com/topic/libraries/architecture/room) From 7bd4fd7cbd9abf0e06901cf76551db1a5f9c95d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Wed, 25 Dec 2019 16:52:17 +0100 Subject: [PATCH 015/236] Version 0.14.0 --- .travis.yml | 2 +- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 10 ++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index bf6f72f78..d6d0cb03a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ cache: branches: only: - develop - - 0.13.0 + - 0.14.0 android: licenses: diff --git a/app/build.gradle b/app/build.gradle index d14972697..d75513767 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 16 targetSdkVersion 29 - versionCode 48 - versionName "0.13.0" + versionCode 49 + versionName "0.14.0" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -122,7 +122,7 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:4e21f7d" + implementation "io.github.wulkanowy:sdk:0.14.0" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0-rc01" diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index fe3993f60..e028c2c45 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,9 +1,7 @@ -Wersja 0.13.0 +Wersja 0.14.0 -- naprawiony plan lekcji po ostatniej aktualizacji VULCANa (19.11) -- naprawiona opcja wyboru motywu dla widżetu ze szczęśliwym numerkiem -- nowa opcja zmiany języka i język rosyjski -- możliwość łatwego nawigowania między dniami w planie lekcji przez kalendarz -- opcja ustawienia modyfikatora oceny na wartość 0,0 +- dodane zostały nowe opcje logowania, m.in. logowanie przez token +- naprawione zostało wyświetlanie nauczyciela, kiedy zastępstwo zostanie usunięte z danej lekcji +- naprawiony został biały ekran w szczęśliwym numerku Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases From 6be801d4a8717b5ca89fabfe0a337a7115aec1c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Mon, 30 Dec 2019 22:57:48 +0100 Subject: [PATCH 016/236] Fix error view which overlaps grade statistics content (#627) --- app/build.gradle | 2 +- .../grade/statistics/GradeStatisticsFragment.kt | 15 ++++++++------- .../grade/statistics/GradeStatisticsPresenter.kt | 5 +++-- .../grade/statistics/GradeStatisticsView.kt | 2 ++ .../attendance/AttendanceRemoteTest.kt | 4 +++- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d75513767..01e60382e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -122,7 +122,7 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:0.14.0" + implementation "io.github.wulkanowy:sdk:069677aa2089b5678adbcae79dc253220995f172" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0-rc01" diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt index 5bcf167e5..a65cab9c9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsFragment.kt @@ -47,6 +47,13 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G override val isBarViewEmpty get() = gradeStatisticsChartPoints.isEmpty + override val currentType + get() = when (gradeStatisticsTypeSwitch.checkedRadioButtonId) { + R.id.gradeStatisticsTypeSemester -> ViewType.SEMESTER + R.id.gradeStatisticsTypePartial -> ViewType.PARTIAL + else -> ViewType.POINTS + } + private lateinit var gradeColors: List> private val vulcanGradeColors = listOf( @@ -272,13 +279,7 @@ class GradeStatisticsFragment : BaseFragment(), GradeStatisticsView, GradeView.G override fun onResume() { super.onResume() - gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId -> - presenter.onTypeChange(when (checkedId) { - R.id.gradeStatisticsTypeSemester -> ViewType.SEMESTER - R.id.gradeStatisticsTypePartial -> ViewType.PARTIAL - else -> ViewType.POINTS - }) - } + gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, _ -> presenter.onTypeChange() } } override fun onSaveInstanceState(outState: Bundle) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt index 9795c130c..bea70ea42 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt @@ -94,7 +94,8 @@ class GradeStatisticsPresenter @Inject constructor( } } - fun onTypeChange(type: ViewType) { + fun onTypeChange() { + val type = view?.let { it.currentType } ?: ViewType.POINTS Timber.i("Select grade stats semester: $type") disposable.clear() view?.run { @@ -214,7 +215,7 @@ class GradeStatisticsPresenter @Inject constructor( private fun showErrorViewOnError(message: String, error: Throwable) { view?.run { - if (isBarViewEmpty || isPieViewEmpty) { + if ((isBarViewEmpty && currentType == ViewType.POINTS) || (isPieViewEmpty) && currentType != ViewType.POINTS) { lastError = error setErrorDetails(message) showErrorView(true) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt index a214744c3..003520fbd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsView.kt @@ -10,6 +10,8 @@ interface GradeStatisticsView : BaseView { val isBarViewEmpty: Boolean + val currentType: ViewType + fun initView() fun updateSubjects(data: ArrayList) diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemoteTest.kt index 318d4ff76..b471dc520 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemoteTest.kt @@ -57,6 +57,7 @@ class AttendanceRemoteTest { subject = "Fizyka", name = "Obecność", date = date, + timeId = 0, number = 0, deleted = false, excusable = false, @@ -65,7 +66,8 @@ class AttendanceRemoteTest { lateness = false, presence = false, categoryId = 1, - absence = false + absence = false, + excuseStatus = null ) } } From 4df245755a93e51b746442749e94687e2d1ea0e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Tue, 31 Dec 2019 17:31:26 +0100 Subject: [PATCH 017/236] Add fields validation in mobile api login (#629) --- app/build.gradle | 2 +- .../ui/modules/login/LoginErrorHandler.kt | 14 ++++++ .../login/advanced/LoginAdvancedFragment.kt | 46 ++++++++++++++++--- .../login/advanced/LoginAdvancedPresenter.kt | 41 +++++++++++++++-- .../login/advanced/LoginAdvancedView.kt | 6 +++ .../res/layout/fragment_login_advanced.xml | 2 - app/src/main/res/values-pl/strings.xml | 4 ++ app/src/main/res/values/strings.xml | 4 ++ 8 files changed, 105 insertions(+), 14 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 01e60382e..f6c2f9ba5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -122,7 +122,7 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:069677aa2089b5678adbcae79dc253220995f172" + implementation "io.github.wulkanowy:sdk:0886d71" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0-rc01" diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt index c888ce79b..75a0ba6a6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginErrorHandler.kt @@ -5,6 +5,10 @@ import android.database.sqlite.SQLiteConstraintException import com.readystatesoftware.chuck.api.ChuckCollector import io.github.wulkanowy.R import io.github.wulkanowy.sdk.exception.BadCredentialsException +import io.github.wulkanowy.sdk.mobile.exception.InvalidPinException +import io.github.wulkanowy.sdk.mobile.exception.InvalidSymbolException +import io.github.wulkanowy.sdk.mobile.exception.InvalidTokenException +import io.github.wulkanowy.sdk.mobile.exception.TokenDeadException import io.github.wulkanowy.ui.base.ErrorHandler import javax.inject.Inject @@ -15,12 +19,22 @@ class LoginErrorHandler @Inject constructor( var onBadCredentials: () -> Unit = {} + var onInvalidToken: (String) -> Unit = {} + + var onInvalidPin: (String) -> Unit = {} + + var onInvalidSymbol: (String) -> Unit = {} + var onStudentDuplicate: (String) -> Unit = {} override fun proceed(error: Throwable) { when (error) { is BadCredentialsException -> onBadCredentials() is SQLiteConstraintException -> onStudentDuplicate(resources.getString(R.string.login_duplicate_student)) + is TokenDeadException -> onInvalidToken(resources.getString(R.string.login_expired_token)) + is InvalidTokenException -> onInvalidToken(resources.getString(R.string.login_invalid_token)) + is InvalidPinException -> onInvalidPin(resources.getString(R.string.login_invalid_pin)) + is InvalidSymbolException -> onInvalidSymbol(resources.getString(R.string.login_invalid_symbol)) else -> super.proceed(error) } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt index 30563ebf4..353effa9b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedFragment.kt @@ -4,7 +4,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.inputmethod.EditorInfo import android.widget.ArrayAdapter +import androidx.appcompat.widget.AppCompatEditText import androidx.core.widget.doOnTextChanged import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Student @@ -84,6 +86,9 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { }) } + loginFormPin.setOnEditorDoneSignIn() + loginFormPass.setOnEditorDoneSignIn() + loginFormSymbol.setAdapter(ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, resources.getStringArray(R.array.symbols_values))) with(loginFormHost) { @@ -92,6 +97,12 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { } } + private fun AppCompatEditText.setOnEditorDoneSignIn() { + setOnEditorActionListener { _, id, _ -> + if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) loginFormSignIn.callOnClick() else false + } + } + override fun setDefaultCredentials(name: String, pass: String, symbol: String, token: String, pin: String) { loginFormName.setText(name) loginFormPass.setText(pass) @@ -101,54 +112,75 @@ class LoginAdvancedFragment : BaseFragment(), LoginAdvancedView { } override fun setErrorNameRequired() { - loginFormNameLayout.run { + with(loginFormNameLayout) { requestFocus() error = getString(R.string.login_field_required) } } override fun setErrorPassRequired(focus: Boolean) { - loginFormPassLayout.run { + with(loginFormPassLayout) { if (focus) requestFocus() error = getString(R.string.login_field_required) } } override fun setErrorPassInvalid(focus: Boolean) { - loginFormPassLayout.run { + with(loginFormPassLayout) { if (focus) requestFocus() error = getString(R.string.login_invalid_password) } } override fun setErrorPassIncorrect() { - loginFormPassLayout.run { + with(loginFormPassLayout) { requestFocus() error = getString(R.string.login_incorrect_password) } } override fun setErrorPinRequired() { - loginFormPinLayout.run { + with(loginFormPinLayout) { requestFocus() error = getString(R.string.login_field_required) } } + override fun setErrorPinInvalid(message: String) { + with(loginFormPinLayout) { + requestFocus() + error = message + } + } + override fun setErrorSymbolRequired() { - loginFormSymbolLayout.run { + with(loginFormSymbolLayout) { requestFocus() error = getString(R.string.login_field_required) } } + override fun setErrorSymbolInvalid(message: String) { + with(loginFormSymbolLayout) { + requestFocus() + error = message + } + } + override fun setErrorTokenRequired() { - loginFormTokenLayout.run { + with(loginFormTokenLayout) { requestFocus() error = getString(R.string.login_field_required) } } + override fun setErrorTokenInvalid(message: String) { + with(loginFormTokenLayout) { + requestFocus() + error = message + } + } + override fun clearNameError() { loginFormNameLayout.error = null } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt index 2cd788b03..1e49e5d8b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedPresenter.kt @@ -24,14 +24,47 @@ class LoginAdvancedPresenter @Inject constructor( view.run { initView() showOnlyScrapperModeInputs() - loginErrorHandler.onBadCredentials = { - setErrorPassIncorrect() - showSoftKeyboard() - Timber.i("Entered wrong username or password") + with(loginErrorHandler) { + onBadCredentials = ::onBadCredentials + onInvalidToken = ::onInvalidToken + onInvalidSymbol = ::onInvalidSymbol + onInvalidPin = ::onInvalidPin } } } + private fun onBadCredentials() { + view?.run { + setErrorPassIncorrect() + showSoftKeyboard() + Timber.i("Entered wrong username or password") + } + } + + private fun onInvalidToken(message: String) { + view?.run { + setErrorTokenInvalid(message) + showSoftKeyboard() + Timber.i("Entered invalid token") + } + } + + private fun onInvalidSymbol(message: String) { + view?.run { + setErrorSymbolInvalid(message) + showSoftKeyboard() + Timber.i("Entered invalid symbol") + } + } + + private fun onInvalidPin(message: String) { + view?.run { + setErrorPinInvalid(message) + showSoftKeyboard() + Timber.i("Entered invalid PIN") + } + } + fun onHostSelected() { view?.apply { clearPassError() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt index ea48d6464..85a85a331 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/advanced/LoginAdvancedView.kt @@ -53,10 +53,16 @@ interface LoginAdvancedView : BaseView { fun setErrorPinRequired() + fun setErrorPinInvalid(message: String) + fun setErrorSymbolRequired() + fun setErrorSymbolInvalid(message: String) + fun setErrorTokenRequired() + fun setErrorTokenInvalid(message: String) + fun showOnlyHybridModeInputs() fun showOnlyScrapperModeInputs() diff --git a/app/src/main/res/layout/fragment_login_advanced.xml b/app/src/main/res/layout/fragment_login_advanced.xml index 094e37d15..15e0904b6 100644 --- a/app/src/main/res/layout/fragment_login_advanced.xml +++ b/app/src/main/res/layout/fragment_login_advanced.xml @@ -188,8 +188,6 @@ android:id="@+id/loginFormToken" android:layout_width="match_parent" android:layout_height="wrap_content" - android:imeActionLabel="@string/login_sign_in" - android:imeOptions="actionDone" android:inputType="textCapCharacters" android:maxLines="1" /> diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index a88a42a79..dd61f7df4 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -36,6 +36,10 @@ Zaloguj To hasło jest za krótkie Dane logowania są niepoprawne + Nieprawidłowy PIN + Nieprawidłowy token + Token stracił ważność + Niepoprawny symbol Nie znaleziono ucznia. Sprawdź symbol To pole jest wymagane Wybrany uczeń jest już zalogowany diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 439bf19d6..e260a4161 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -36,6 +36,10 @@ Sign in This password is too short Login details are incorrect + Invalid PIN + Invalid token + Token expired + Invalid symbol Student not found. Check the symbol This field is required The selected student is already logged in From a45c7bd3e34129e2c24f955df402a82e857b5c63 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2019 22:04:35 +0000 Subject: [PATCH 018/236] Bump dagger from 2.25.3 to 2.25.4 (#632) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index f6c2f9ba5..a9bfb29dc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -112,7 +112,7 @@ play { ext { work_manager = "2.3.0-beta02" room = "2.2.3" - dagger = "2.25.3" + dagger = "2.25.4" chucker = "2.0.4" mockk = "1.9.2" } From 8a6ceeb2e4520f68d794b3b31ef0694c95af5ec8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2020 19:42:15 +0000 Subject: [PATCH 019/236] Bump aboutlibraries from 7.0.4 to 7.1.0 (#633) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index a9bfb29dc..fe8e0c040 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -174,7 +174,7 @@ dependencies { implementation "com.jakewharton.timber:timber:4.7.1" implementation "at.favre.lib:slf4j-timber:1.0.1" implementation "com.squareup.okhttp3:logging-interceptor:3.12.6" - implementation "com.mikepenz:aboutlibraries:7.0.4" + implementation "com.mikepenz:aboutlibraries-core:7.1.0" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' playImplementation "com.google.firebase:firebase-core:17.2.1" From b9378c24b5356eb232b351936e26df190c0463a0 Mon Sep 17 00:00:00 2001 From: pcktm <13273420+pcktm@users.noreply.github.com> Date: Fri, 3 Jan 2020 21:12:05 +0100 Subject: [PATCH 020/236] Minor translation changes (#630) --- README.en.md | 12 ++++++------ README.md | 10 +++++----- app/src/main/res/values-pl/strings.xml | 4 ++-- app/src/main/res/values/preferences_values.xml | 2 +- app/src/main/res/values/strings.xml | 18 +++++++++--------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/README.en.md b/README.en.md index 4da00e5dd..7444cccae 100644 --- a/README.en.md +++ b/README.en.md @@ -7,11 +7,11 @@ [![F-Droid](https://img.shields.io/f-droid/v/io.github.wulkanowy.svg?style=flat-square)](https://f-droid.org/packages/io.github.wulkanowy/) [![Last release](https://img.shields.io/github/release/wulkanowy/wulkanowy.svg?logo=github&style=flat-square)](https://github.com/wulkanowy/wulkanowy/releases) -Unofficial android VULCAN UONET+ register client for student and parent +Unofficial android VULCAN UONET+ register client for both students and their parents ## Features -* logging in using the email and password +* logging in using the email and password OR using token and pin * functions from the register website: * grades * grade statistics @@ -24,7 +24,7 @@ Unofficial android VULCAN UONET+ register client for student and parent * homework * notes * lucky number -* calculation of the average +* calculation of the average independently of school's preferences * notifications, e.g. about a new grade * dark and black (AMOLED) theme * offline mode @@ -32,16 +32,16 @@ Unofficial android VULCAN UONET+ register client for student and parent ## Download -You can download the current beta from the Google Play or Fdroid store +You can download the current beta version from the Google Play or the F-Droid store [Get it on Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) [Get it on Fdroid](https://f-droid.org/packages/io.github.wulkanowy/) -You can also download a [development version](https://wulkanowy.github.io/#download) that includes new features prepared for the next release +You can also download a [development version](https://wulkanowy.github.io/#download) that includes new features being prepared for the next release ## Built With diff --git a/README.md b/README.md index d51115d5e..61b13444a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica ## Funkcje -* logowanie za pomocą e-maila i hasła +* logowanie za pomocą e-maila i hasła LUB tokena i pinu * funkcje ze strony internetowej dziennika: * oceny * statystyki ocen @@ -24,7 +24,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica * zadania domowe * uwagi * szczęśliwy numerek -* obliczanie średniej +* obliczanie średniej niezależnie od preferencji szkoły * powiadomienia np. o nowej ocenie * ciemny i czarny (AMOLED) motyw * tryb offilne @@ -32,13 +32,13 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica ## Pobierz -Aktualną wersję beta możesz pobrać ze sklepu Google Play lub Fdroid +Aktualną wersję beta możesz pobrać ze sklepu Google Play lub F-Droid [Pobierz z Google Play](https://play.google.com/store/apps/details?id=io.github.wulkanowy) [Pobierz z Fdroid](https://f-droid.org/packages/io.github.wulkanowy/) @@ -59,4 +59,4 @@ Wnieś swój wkład w projekt, tworząc PR lub wysyłając issue na GitHub. ## Licencja -Ten projekt jest licencjonowany w ramach Apache License 2.0 - szczegóły w pliku [LICENSE](LICENSE) \ No newline at end of file +Ten projekt udostępniany jest na licencji Apache License 2.0 - szczegóły w pliku [LICENSE](LICENSE) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index dd61f7df4..cedb224bb 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -359,8 +359,8 @@ Brak połączenia z internetem - Zbyt długie oczekiwanie na połączenie z dziennikiem - Logowanie nie powiodło się. Spróbuj ponownie lub zrestartuj aplikację + Upłynął limit czasu na połączenie z dziennikiem + Logowanie nie powiodło się. Zrestartuj aplikację i spróbuj ponownie Dziennik jest niedostępny. Spróbuj ponownie później Wystąpił nieoczekiwany błąd Funkcja wyłączona przez szkołę diff --git a/app/src/main/res/values/preferences_values.xml b/app/src/main/res/values/preferences_values.xml index 05210fd22..ee54d53c6 100644 --- a/app/src/main/res/values/preferences_values.xml +++ b/app/src/main/res/values/preferences_values.xml @@ -83,7 +83,7 @@ - Average grades from the 2nd semester + Average of grades only from the 2nd semester Average of grades from the whole year diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e260a4161..7d568b9b7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -27,14 +27,14 @@ Password Register Mobile API - Scrapper + Scraper Hybrid Token PIN API key Symbol Sign in - This password is too short + Password too short Login details are incorrect Invalid PIN Invalid token @@ -42,7 +42,7 @@ Invalid symbol Student not found. Check the symbol This field is required - The selected student is already logged in + Selected student is already logged in The symbol can be found on the register page in Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne Select students to log in to the application Other options @@ -210,7 +210,7 @@ Mobile devices No devices - Unregister + Deregister Device removed QR code Token @@ -243,7 +243,7 @@ Add account Logout - Do you want to log out of an active student? + Do you want to log out of this student account? Student logout @@ -309,7 +309,7 @@ Synchronization Automatic update - Suspended on holiday + Suspended on holidays Updates interval Wi-Fi only @@ -340,9 +340,9 @@ No internet connection - Too long waiting time for connection to the register - Login is failed. Try again or restart the app - The log is not available. Try again later + Connection to the register timed out + Login failed. Restart the app and try again + The service is not available. Try again later An unexpected error occurred Feature disabled by your school Feature not available in this mode From f204264d2da69c1ca5a9a373c5927d3df8ef995c Mon Sep 17 00:00:00 2001 From: Dominik Korsa Date: Sat, 4 Jan 2020 01:46:43 +0100 Subject: [PATCH 021/236] Change grade header note to unread count (#634) --- .../modules/grade/details/GradeDetailsHeader.kt | 1 + .../res/drawable/background_header_note.xml | 5 +++++ .../main/res/layout/header_grade_details.xml | 17 +++++++++++------ 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/drawable/background_header_note.xml diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt index e5f6e8249..9ed412808 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsHeader.kt @@ -38,6 +38,7 @@ class GradeDetailsHeader( gradeHeaderAverage.text = average gradeHeaderNumber.text = number gradeHeaderNote.visibility = if (newGrades > 0) VISIBLE else GONE + if (newGrades > 0) gradeHeaderNote.text = newGrades.toString(10) gradeHeaderContainer.isEnabled = isExpandable isViewExpandable = isExpandable diff --git a/app/src/main/res/drawable/background_header_note.xml b/app/src/main/res/drawable/background_header_note.xml new file mode 100644 index 000000000..b6555847b --- /dev/null +++ b/app/src/main/res/drawable/background_header_note.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/header_grade_details.xml b/app/src/main/res/layout/header_grade_details.xml index 40f486cf5..0fa3872e1 100644 --- a/app/src/main/res/layout/header_grade_details.xml +++ b/app/src/main/res/layout/header_grade_details.xml @@ -7,7 +7,7 @@ android:background="?selectableItemBackground" android:paddingLeft="16dp" android:paddingTop="10dp" - android:paddingRight="12dp" + android:paddingRight="14dp" android:paddingBottom="10dp" tools:context=".ui.modules.grade.details.GradeDetailsHeader" android:paddingEnd="12dp" @@ -50,14 +50,19 @@ android:textSize="12sp" tools:text="12 grades" /> - + android:background="@drawable/background_header_note" + android:textColor="?colorOnPrimary" + android:textSize="14sp" + tools:text="255" /> From e0657eb5b2375b1c1ccf6a7da4493b5e45565060 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2020 22:48:37 +0000 Subject: [PATCH 022/236] Bump work_manager from 2.3.0-beta02 to 2.3.0-rc01 (#638) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index fe8e0c040..f5a95068d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -110,7 +110,7 @@ play { } ext { - work_manager = "2.3.0-beta02" + work_manager = "2.3.0-rc01" room = "2.2.3" dagger = "2.25.4" chucker = "2.0.4" From 70166d024502e676605c5213b8ca4fbfd22b7551 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2020 22:50:35 +0000 Subject: [PATCH 023/236] Bump fragment-ktx from 1.2.0-rc04 to 1.2.0-rc05 (#639) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index f5a95068d..3b9514014 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -129,7 +129,7 @@ dependencies { implementation "androidx.activity:activity-ktx:1.1.0-rc03" implementation "androidx.appcompat:appcompat:1.1.0" implementation "androidx.appcompat:appcompat-resources:1.1.0" - implementation "androidx.fragment:fragment-ktx:1.2.0-rc04" + implementation "androidx.fragment:fragment-ktx:1.2.0-rc05" implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.multidex:multidex:2.0.1" From 69a8e35150a2d954178e682184400925a094c28b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2020 23:05:59 +0000 Subject: [PATCH 024/236] Bump logging-interceptor from 3.12.6 to 3.12.7 (#641) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 3b9514014..4ef02ffc2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -173,7 +173,7 @@ dependencies { implementation "com.jakewharton.threetenabp:threetenabp:1.2.1" implementation "com.jakewharton.timber:timber:4.7.1" implementation "at.favre.lib:slf4j-timber:1.0.1" - implementation "com.squareup.okhttp3:logging-interceptor:3.12.6" + implementation "com.squareup.okhttp3:logging-interceptor:3.12.7" implementation "com.mikepenz:aboutlibraries-core:7.1.0" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' From 15184550f4b153b3bdba4aa895ab1f6a8c5a8a6c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2020 23:25:02 +0000 Subject: [PATCH 025/236] Bump junit from 4.12 to 4.13 (#640) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 4ef02ffc2..cb252e66e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -185,7 +185,7 @@ dependencies { debugImplementation "fr.o80.chucker:library:$chucker" debugImplementation "com.amitshekhar.android:debug-db:1.0.6" - testImplementation "junit:junit:4.12" + testImplementation "junit:junit:4.13" testImplementation "io.mockk:mockk:$mockk" testImplementation "org.threeten:threetenbp:1.4.0" testImplementation "org.mockito:mockito-inline:3.2.4" From d706c000b92743c39b54b83448ee501cf0d79658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Thu, 9 Jan 2020 00:42:16 +0100 Subject: [PATCH 026/236] Add missing translations (#642) --- app/src/main/res/values-pl/strings.xml | 4 ++-- app/src/main/res/values-ru/strings.xml | 15 +++++++++++++++ app/src/main/res/values/strings.xml | 7 ++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index cedb224bb..a000ad695 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -28,7 +28,7 @@ Dziennik Symbol Mobilne API - Scrapper + Scraper Hybrydowe Token PIN @@ -360,7 +360,7 @@ Brak połączenia z internetem Upłynął limit czasu na połączenie z dziennikiem - Logowanie nie powiodło się. Zrestartuj aplikację i spróbuj ponownie + Logowanie nie powiodło się. Spróbuj ponownie lub zrestartuj aplikację Dziennik jest niedostępny. Spróbuj ponownie później Wystąpił nieoczekiwany błąd Funkcja wyłączona przez szkołę diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 28f4831dc..53430b664 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -28,15 +28,26 @@ Электронная почта или имя пользователя Пароль Дневник + Мобильный API + Scraper + Гибрид + Token + PIN + клавиша API Symbol Войти Слишком короткий пароль Указаны неверные данные + Недействительный PIN + Недействительный token + Токен просрочен + Недействительный symbol Не удалось найти ученика. Пожалуйста, проверьте \"symbol\" Это поле обязательно Данный ученик уже авторизован Вы можете найти \"symbol\" в Uczeń -> Dostęp Mobilny -> Zarejestruj urządzenie mobilne Выберите учеников для авторизации в приложении + Другие варианты Политика приватности Проблемы с авторизацией? Свяжитесь с нами! Электронная почта @@ -261,6 +272,8 @@ Версия приложения Сообщить о ошибке Отправить сообщение о ошибке через электронную почту + FAQ + Читайте часто задаваемые вопросы Сервер Discord Присоединиться к сообществу приложения Политика приватности @@ -277,6 +290,7 @@ Содержание + Снова Описание Нет описания Учитель @@ -349,5 +363,6 @@ Дневник недоступен. Попробуйте позже Произошла неожиданная ошибка Функция была выключена школой + Функция не доступна в этом режиме diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7d568b9b7..72b9a5d4f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -243,7 +243,7 @@ Add account Logout - Do you want to log out of this student account? + Do you want to log out of an active student? Student logout @@ -341,9 +341,10 @@ No internet connection Connection to the register timed out - Login failed. Restart the app and try again - The service is not available. Try again later + Login failed. Try again or restart the app + The register is not available. Try again later An unexpected error occurred Feature disabled by your school Feature not available in this mode + From 2b5b87fe84d0a3fdbddec9b8c59ecd5fbcb8cb26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Thu, 9 Jan 2020 00:53:32 +0100 Subject: [PATCH 027/236] Version 0.14.1 --- .travis.yml | 2 +- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index d6d0cb03a..d37308816 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ cache: branches: only: - develop - - 0.14.0 + - 0.14.1 android: licenses: diff --git a/app/build.gradle b/app/build.gradle index cb252e66e..452ddfb67 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 16 targetSdkVersion 29 - versionCode 49 - versionName "0.14.0" + versionCode 50 + versionName "0.14.1" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -122,7 +122,7 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:0886d71" + implementation "io.github.wulkanowy:sdk:0.14.1" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0-rc01" diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index e028c2c45..47c30e792 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,7 +1,8 @@ -Wersja 0.14.0 +Wersja 0.14.1 -- dodane zostały nowe opcje logowania, m.in. logowanie przez token -- naprawione zostało wyświetlanie nauczyciela, kiedy zastępstwo zostanie usunięte z danej lekcji -- naprawiony został biały ekran w szczęśliwym numerku +- naprawiliśmy logowanie użytkownikom, którzy mieli przypisanych do konta uczniów ze szkół z wyłączonymi dziennikami +- naprawiliśmy problemy z wysyłaniem wiadomości i odświeżaniem ocen spowodowanych ostatnią aktualizacją +- dodaliśmy informację o liczbie nieprzeczytanych ocen w nagłówku przedmiotu +- ulepszyliśmy informacje o błędach przy logowaniu w trybie mobilnym Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases From 57e943fb3bdeeffd1320b27dcc7355d16654e9e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 11 Jan 2020 17:25:13 +0100 Subject: [PATCH 028/236] Update error_service_unavailable string (#645) --- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index a000ad695..0972138fa 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -361,7 +361,7 @@ Brak połączenia z internetem Upłynął limit czasu na połączenie z dziennikiem Logowanie nie powiodło się. Spróbuj ponownie lub zrestartuj aplikację - Dziennik jest niedostępny. Spróbuj ponownie później + Trwa przerwa techniczna dziennika UONET+. Spróbuj ponownie później Wystąpił nieoczekiwany błąd Funkcja wyłączona przez szkołę Funkcja niedostępna w tym trybie diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 53430b664..0499cbf55 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -360,7 +360,7 @@ Нет интернет-подключения Слишком долгое ожидание соединения с дневником Авторизация не удалась. Пожалуйста, попробуйте ещё раз или перезапустите дневник - Дневник недоступен. Попробуйте позже + Технический перерыв в журнале UONET + продолжается. Попробуйте позже Произошла неожиданная ошибка Функция была выключена школой Функция не доступна в этом режиме diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 72b9a5d4f..c8b94bff5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -342,7 +342,7 @@ No internet connection Connection to the register timed out Login failed. Try again or restart the app - The register is not available. Try again later + Maintenance underway UONET + register. Try again later An unexpected error occurred Feature disabled by your school Feature not available in this mode From 83a8c857e5876ca9120231422beffc051009dbe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 11 Jan 2020 20:09:33 +0100 Subject: [PATCH 029/236] =?UTF-8?q?Change=20color=20attr=20in=20backtround?= =?UTF-8?q?=5Fheader=5Fnote=20drawable=20to=20color=20re=E2=80=A6=20(#644)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/res/drawable-night/background_header_note.xml | 5 +++++ app/src/main/res/drawable/background_header_note.xml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable-night/background_header_note.xml diff --git a/app/src/main/res/drawable-night/background_header_note.xml b/app/src/main/res/drawable-night/background_header_note.xml new file mode 100644 index 000000000..2983b32c6 --- /dev/null +++ b/app/src/main/res/drawable-night/background_header_note.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/background_header_note.xml b/app/src/main/res/drawable/background_header_note.xml index b6555847b..8e92e3abf 100644 --- a/app/src/main/res/drawable/background_header_note.xml +++ b/app/src/main/res/drawable/background_header_note.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file From e8b4e163827327c73cdc3cf17bcce0c6a018a489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sat, 11 Jan 2020 20:10:23 +0100 Subject: [PATCH 030/236] Version 0.14.2 --- .travis.yml | 2 +- app/build.gradle | 7 +++---- app/src/main/play/release-notes/pl-PL/default.txt | 8 +++----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index d37308816..fa81da66b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ cache: branches: only: - develop - - 0.14.1 + - 0.14.2 android: licenses: diff --git a/app/build.gradle b/app/build.gradle index 452ddfb67..24a470ae0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 16 targetSdkVersion 29 - versionCode 50 - versionName "0.14.1" + versionCode 51 + versionName "0.14.2" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true @@ -122,7 +122,7 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:0.14.1" + implementation "io.github.wulkanowy:sdk:0.14.2" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0-rc01" @@ -173,7 +173,6 @@ dependencies { implementation "com.jakewharton.threetenabp:threetenabp:1.2.1" implementation "com.jakewharton.timber:timber:4.7.1" implementation "at.favre.lib:slf4j-timber:1.0.1" - implementation "com.squareup.okhttp3:logging-interceptor:3.12.7" implementation "com.mikepenz:aboutlibraries-core:7.1.0" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 47c30e792..467c3ec82 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,8 +1,6 @@ -Wersja 0.14.1 +Wersja 0.14.2 -- naprawiliśmy logowanie użytkownikom, którzy mieli przypisanych do konta uczniów ze szkół z wyłączonymi dziennikami -- naprawiliśmy problemy z wysyłaniem wiadomości i odświeżaniem ocen spowodowanych ostatnią aktualizacją -- dodaliśmy informację o liczbie nieprzeczytanych ocen w nagłówku przedmiotu -- ulepszyliśmy informacje o błędach przy logowaniu w trybie mobilnym +- naprawiliśmy błąd powodujący zatrzymanie aplikacji na urządzeniach z Androidem poniżej 5.0 +- zmieniliśmy mało mówiący komunikat o niedostępnym dzienniku na taki mówiący o przerwie technicznej dziennika Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases From 4d1de2d8ce6856928c0ea964c264b279e68e70e8 Mon Sep 17 00:00:00 2001 From: Dominik Korsa Date: Sun, 12 Jan 2020 14:21:33 +0100 Subject: [PATCH 031/236] Add debug notification icons (#637) --- .../res/drawable-anydpi-v24/ic_stat_grade.xml | 12 ++++++++++++ .../drawable-anydpi-v24/ic_stat_luckynumber.xml | 12 ++++++++++++ .../res/drawable-anydpi-v24/ic_stat_message.xml | 12 ++++++++++++ .../res/drawable-anydpi-v24/ic_stat_note.xml | 12 ++++++++++++ .../debug/res/drawable-hdpi/ic_stat_grade.png | Bin 0 -> 478 bytes .../res/drawable-hdpi/ic_stat_luckynumber.png | Bin 0 -> 652 bytes .../debug/res/drawable-hdpi/ic_stat_message.png | Bin 0 -> 384 bytes app/src/debug/res/drawable-hdpi/ic_stat_note.png | Bin 0 -> 460 bytes .../debug/res/drawable-mdpi/ic_stat_grade.png | Bin 0 -> 355 bytes .../res/drawable-mdpi/ic_stat_luckynumber.png | Bin 0 -> 451 bytes .../debug/res/drawable-mdpi/ic_stat_message.png | Bin 0 -> 297 bytes app/src/debug/res/drawable-mdpi/ic_stat_note.png | Bin 0 -> 339 bytes .../debug/res/drawable-xhdpi/ic_stat_grade.png | Bin 0 -> 588 bytes .../res/drawable-xhdpi/ic_stat_luckynumber.png | Bin 0 -> 897 bytes .../debug/res/drawable-xhdpi/ic_stat_message.png | Bin 0 -> 508 bytes .../debug/res/drawable-xhdpi/ic_stat_note.png | Bin 0 -> 586 bytes .../debug/res/drawable-xxhdpi/ic_stat_grade.png | Bin 0 -> 781 bytes .../res/drawable-xxhdpi/ic_stat_luckynumber.png | Bin 0 -> 1329 bytes .../res/drawable-xxhdpi/ic_stat_message.png | Bin 0 -> 692 bytes .../debug/res/drawable-xxhdpi/ic_stat_note.png | Bin 0 -> 805 bytes .../res/drawable-anydpi-v24/ic_stat_grade.xml | 10 +++------- .../drawable-anydpi-v24/ic_stat_luckynumber.xml | 10 +++------- .../res/drawable-anydpi-v24/ic_stat_message.xml | 10 +++------- .../res/drawable-anydpi-v24/ic_stat_note.xml | 10 +++------- app/src/main/res/drawable/ic_main_grade.xml | 6 ++++-- app/src/main/res/drawable/ic_main_more.xml | 6 ++++-- 26 files changed, 68 insertions(+), 32 deletions(-) create mode 100644 app/src/debug/res/drawable-anydpi-v24/ic_stat_grade.xml create mode 100644 app/src/debug/res/drawable-anydpi-v24/ic_stat_luckynumber.xml create mode 100644 app/src/debug/res/drawable-anydpi-v24/ic_stat_message.xml create mode 100644 app/src/debug/res/drawable-anydpi-v24/ic_stat_note.xml create mode 100644 app/src/debug/res/drawable-hdpi/ic_stat_grade.png create mode 100644 app/src/debug/res/drawable-hdpi/ic_stat_luckynumber.png create mode 100644 app/src/debug/res/drawable-hdpi/ic_stat_message.png create mode 100644 app/src/debug/res/drawable-hdpi/ic_stat_note.png create mode 100644 app/src/debug/res/drawable-mdpi/ic_stat_grade.png create mode 100644 app/src/debug/res/drawable-mdpi/ic_stat_luckynumber.png create mode 100644 app/src/debug/res/drawable-mdpi/ic_stat_message.png create mode 100644 app/src/debug/res/drawable-mdpi/ic_stat_note.png create mode 100644 app/src/debug/res/drawable-xhdpi/ic_stat_grade.png create mode 100644 app/src/debug/res/drawable-xhdpi/ic_stat_luckynumber.png create mode 100644 app/src/debug/res/drawable-xhdpi/ic_stat_message.png create mode 100644 app/src/debug/res/drawable-xhdpi/ic_stat_note.png create mode 100644 app/src/debug/res/drawable-xxhdpi/ic_stat_grade.png create mode 100644 app/src/debug/res/drawable-xxhdpi/ic_stat_luckynumber.png create mode 100644 app/src/debug/res/drawable-xxhdpi/ic_stat_message.png create mode 100644 app/src/debug/res/drawable-xxhdpi/ic_stat_note.png diff --git a/app/src/debug/res/drawable-anydpi-v24/ic_stat_grade.xml b/app/src/debug/res/drawable-anydpi-v24/ic_stat_grade.xml new file mode 100644 index 000000000..832eba838 --- /dev/null +++ b/app/src/debug/res/drawable-anydpi-v24/ic_stat_grade.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable-anydpi-v24/ic_stat_luckynumber.xml b/app/src/debug/res/drawable-anydpi-v24/ic_stat_luckynumber.xml new file mode 100644 index 000000000..4f3eb98e1 --- /dev/null +++ b/app/src/debug/res/drawable-anydpi-v24/ic_stat_luckynumber.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable-anydpi-v24/ic_stat_message.xml b/app/src/debug/res/drawable-anydpi-v24/ic_stat_message.xml new file mode 100644 index 000000000..8fe12de45 --- /dev/null +++ b/app/src/debug/res/drawable-anydpi-v24/ic_stat_message.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable-anydpi-v24/ic_stat_note.xml b/app/src/debug/res/drawable-anydpi-v24/ic_stat_note.xml new file mode 100644 index 000000000..d30f22336 --- /dev/null +++ b/app/src/debug/res/drawable-anydpi-v24/ic_stat_note.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/debug/res/drawable-hdpi/ic_stat_grade.png b/app/src/debug/res/drawable-hdpi/ic_stat_grade.png new file mode 100644 index 0000000000000000000000000000000000000000..013b7ac4988d5b4215636dbf10fd4f9666f2c4c0 GIT binary patch literal 478 zcmeAS@N?(olHy`uVBq!ia0y~yU{C>J4mJh`hKCF@W-u@?F7)IbhDQr8OxKB}FIYXh$Ss9nt(-ali zUovfE+BhSO?~>}~&>QV3d3IYw+8@>_@UbVEO_F61o$sJoT{lVAB{8VMGJ|iD1iyg&(a`q?MK&s%?pQYA z=7jG`#~cGbW(It``=h3~?~vX(IUyOA)oFPTiYJ(@xX*H={zSJ4mJh`hKCF@W-u@?1$(+UhE&{oJHuPU)luU3 z`tVPBowm~4(yBp0pEPc2v`o<8PHNusIH_@pIk$AnCy$iHIunwTlOImi-85m!@k5_~ z_}4B!Rw`%weAo47bzg6vnK|>fwekI*&*o%rt~?|>Z^KOyS5Fq_1hy+p3=i0~R^+lQ z+3DT(_eJ|B<8ucVezLw5qbtlMC;Bnrk>NTKuh1upuS|8?DAw{RM)L{lI@OdL-`f5z zb&&92^gPgh%S)w2YUj>HnWqoWt(-M)=gIXBg0ZuOCPXe5J)l-a+@6ta*U2|>bbD!z%(6(c!QH$*G_!V zaNvin6S7vZlNPY`P-`nYeN#1o@78~bEhi6VOuDR2oA*ncR3wx8(vg+ClQf6syMq6(&eGgi%UZNm&sf#r@TI`33pjX)pchOx-uf8nS&^~%@PhHH66LT_p zf?NA`nuR^xzT$7+(C|HWBc8khki>-1uEl{*u;7 zW-I?Pw)|J4mJh`hKCF@W-u@?x_i1fhE&{2PLN<-oUo+f z|NsBd%pXe>=J}`EaChwWDD6=d`2W*CTEMHTIqlDK+Z%pe&0&`hIxcUU+bqUZXft!i zd@;UL3lxt3;<095E18qYF}wL!!;MCt7*B3?-d!5fC5k35E?rpAZR90eBGd7@(SonT z)*)$$#D-~Is#}FjZ#iYJoZ_do;;O~sw&tvfXSiG4#EtmalJcr7UvLN*3tg#3ACMCBTe`rnm4W5kzNkLM2MPfie_KzP3O!cnl5IZk7_f8whFy~;G_QChA>_0|az10- zl_DNS_L&N*y#-a@nl?T)N>jMUB3jRXWDoO0g$TUTYZ`7#KWV{an^LB{Ts5DOj2> literal 0 HcmV?d00001 diff --git a/app/src/debug/res/drawable-hdpi/ic_stat_note.png b/app/src/debug/res/drawable-hdpi/ic_stat_note.png new file mode 100644 index 0000000000000000000000000000000000000000..1992edf256bdb7f24a1a1404c4a0adf20d4ed656 GIT binary patch literal 460 zcmeAS@N?(olHy`uVBq!ia0y~yU{C>J4mJh`hKCF@W-u@?PWE(h45_%4oFKuvI6yKZHZx z$O&i*boM)at#LJaZmr6C!$Ybh!hGlKX45lM#4rh9n@jYAcJmBkFhZ<4OcokFG)W(zo z13ps&_E?ARe{TamUUxWSx`3~#YqsJ`-i2-@XIURbd$idxK4K7(Ins26Gh@jS!-o!k z*xjAZDqV97*ttGnr=6Ef3BNyIwvutnEKSYsZ;qSI%}1CrE62@c;k+ zNe(i+M?Shd5>R-ic#q3-Mal7fZ^?w*BGw;FCVdhB8BsDEr6BC~1&e~0fvu_Fst z3bG_st2nbxc(RP~qkN5+i!<-m18IyC*0LX&f3fMv#93lfoQ`r$Pzr0BB^KgvbV~)oqATgB~awp zah{pIlASdol4*u!J|{2DPiWLoUK4TSr}Y}08{S<@*DrC&d7|yw%$k~>n4GMGtO3I%gzaSIoVap=Q1bj z+XWN#q}@9+_~l>x6}{fOoHyetbBtV9&>c3bcL$Hmzxy(N(Vgq5g%u0y85kHCJYD@< J);T3K0RYQV%ZUI0 literal 0 HcmV?d00001 diff --git a/app/src/debug/res/drawable-mdpi/ic_stat_message.png b/app/src/debug/res/drawable-mdpi/ic_stat_message.png new file mode 100644 index 0000000000000000000000000000000000000000..7327a02f32c857a246a430d5dccf26d8d27b8552 GIT binary patch literal 297 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJoK6tt~hE&{2PLN<-()9oT z|7gZSQGx4|JF**ZbWV6U&Ed~``_u#M^I2Q}YqGEXp`)>=i8c2~)Q85FirwCQtnrN& z^F01^$#CzRJfZ0x?GS#Ol4;IMA`IkLxr=%7gq)ef`Y6Clm%(+F$Sl^|3oDb9r^x%9X!3Me)4=Sl{IW6f z0`F5}XXU>(cHC+c1=eVYIUEgR`Y1mk^+lJrhoMYAtM&r14GXlx)D|~6@hhn*>|;JM zePt-~AsvRDx5TAv6a-vzxf33+2^_z)wvU}*^4o{T%o<)F7#J8BJYD@<);T3K0RYJf BbAtc? literal 0 HcmV?d00001 diff --git a/app/src/debug/res/drawable-mdpi/ic_stat_note.png b/app/src/debug/res/drawable-mdpi/ic_stat_note.png new file mode 100644 index 0000000000000000000000000000000000000000..2fb0209825a5ba1b4016d0b288471114db74e2e4 GIT binary patch literal 339 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4mJh`h6m-gKNuJor9536Ln>}1CrGd^Y54#D ze>7vEtx%boQ$Mf3b)lu~8j+vmgK zUr!y*_%on-9DPcW=e4T%%;#b_8DH~=suxc+5+aQqK$QPx`a!9hGse`lQJ;TS;tgB51+z#u$ rwX*SZxCGv2l3-AM8L{i?XNDtrw`WxB2${pcz`)??>gTe~DWM4fdqsnB literal 0 HcmV?d00001 diff --git a/app/src/debug/res/drawable-xhdpi/ic_stat_grade.png b/app/src/debug/res/drawable-xhdpi/ic_stat_grade.png new file mode 100644 index 0000000000000000000000000000000000000000..c63f810fb059d540b0d582b4ea9751038ae9a6e1 GIT binary patch literal 588 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?Fw9nPYY!G?RV%Qb{E+P+L#JDaVji;@B1BTV%s9C7m_N+{W|hglTd&`cgdPR?0D) z>SX>{$t1a@(x5s)QBd{Ff^P?sRZ7zfLc%6dH z^!y_ya6P!;F54kyw-BlKJGqY>@^}`-__0>!2a7_lxIbfhg47Cf5A zo)IxQiQnOAazlmM72_q3s&t+yRzHxPbjvtHZl;eY{{@AUO4SdPX3b$2sCS;UT|P*z z;na2Illdyg4&-`BNln<*G9}5-$aX=S3V#=Gnv>iNuf-RiT}`QCFj4;~?%8Xj$p51G zmZHgr14czQ456P{J$uCrPo5AovEx5-f8~qDSdoec;*+GF9t`^|?B!&XQOa~Y;Io6; zq(2)E-uQMXoL^(n*C$EryV>O&fAeKtxOuAIqv&@>o*&Nw_5#KUk6GEWKg&*McTj!c r7_doliemp0Mn0Xb+c*w;Ffg3=l3f}z-GH5efq}u()z4*}Q$iB}=cMf! literal 0 HcmV?d00001 diff --git a/app/src/debug/res/drawable-xhdpi/ic_stat_luckynumber.png b/app/src/debug/res/drawable-xhdpi/ic_stat_luckynumber.png new file mode 100644 index 0000000000000000000000000000000000000000..4035ceba82f220009e33b2828d831a26219af5f5 GIT binary patch literal 897 zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F%S=as`doU@{-s(!EmXNZepgQAh%DeK(~;fbbsrHm7*UbID~{8 z&n)6rT+21FVyOvFVDVr7Pj{a@S@Qi{WpDM#nv?pu?<()SuYb1t{?BuJCwd&@3fs^m zs*_PHlWtKeHdjJ{?r$kw`cF1Kl}O2_uRfZSI# zk}p0NKC+GQtc`XIyYy3}HoYnPp#vdln-Y+M@_RTvM-lX=cep}iu+k{#F*Js|DvbJdHhVU<$ zXFl`vnD{PDH_1D5C{5S=IMapohUa|)QXc)0>YbgobH@Bv@3#He?PK&gB_n@_v&jsO z7ncMcq|LT`!29%Q0RR?ec`*#;ArlpG1v1ND}QP| znx|!P{{04Kxz%sp-N;o@ntDA;Wb*uJCI1Y*#?MtgH}i7tmGW!%H5O~Xc-t6%?*7TO zHEu=6`#8U^{CYAi^Vd?fmt8A9uaJ=6xpUs7C*mjLyQiuzoy2tHl*vNvMN^DU)NSC` zd3wvOQJm|%*J**z-AsDTpC9ZCFyAP$_hi`&+3eIU%0aWxN1j@YK)iM))VT}-QZ zSWLKTkKGao;F+h*5s|0hprdpACf8!A`N3hcvShDMVq+3=iZr^DrZtysUBIlAZ&ETT z?Sf*f6U-}^{G>P@TrMrYvG2nZuf;jawWqhr|4nDPAa`KC_r6BWMDYV&oy8*49(6a! zCjRlz2tKpX`Qo|_bx+rv4ct2~NnZHypPAcQjAnY6eA<6o{uTGMODmF3*)T9LFnGH9 KxvX}1CrGd^P7vv7 z3Uu)P@c)0ku);l#Bi~>4d}r8bzO>Jr?NR)HcCnVgM~%Gy+2!smIl}y%t&#m7qoMjv zxr$Hq1q}ZJ`4=?aH_<5FckvLjf1$*W32aBdz6<2hU)?c{iT=@uA=(^Zo_7i@xP!Gr zm93jyj>(XJqMQTU{056Eg9-(?Zrc}f5sXK;7nB}0-y+y`qVdZC>l|m52pfey6?LW~ z))%}S483eONOZ`aI8bHr=HN@l6*l^_qNO^&9^lsLi}!fDsDA?cf~;Q?p6E|{-PpQC zV|(Wp2cD>xN1Rr%i(OiEN%0bMTt*g)=f?w1QP*a;R2>lZ?G@N4w_xRC&kqhJlm5sn zq^TZzkQZe=kA?q>!pV5qoXrj`U4eO9^wOVC_{gw@FQQ%LyJ$x$`)>9y2kT$jjX!v2 z#T-kRQY}>tx)CsauDhmn|C6axMYyfs3$?xwn|S== ze5tRdJHN}7KeKp#@7e16&+3ZP_ua2OGiPRJ$AyK??R>Ift%(x9AKYQTy&&$O#L|SL z=Pi1cBv|h^Wpdx`^3n9c-6Ikw!?^Y|98vgfZ+xazsFC-=aY;e88A_I$ceL82dFEZ@ zYLxp_wQZr@D$oDCQ4ZW0jJgY0w={@xur~%@I?4EJjA}!IPec= zhqkDhT!GS2g7GFQ1TAH=kW*Ve~+)2fVbGwz6^Kh@~yh zxO#MZ({=`9&y6h(b1$fcrhZg?aQH|_gr;JE_7=}zO@ovo#_Wj#8A_Gk5|6GBi7?YN z&{Q<=`E=ZGHz$`#uUbR)r1JJ2hIr;_9`7VmEMpV;)+>L05O;OzwR)}}x5~;azM5T` u&#-%`411;DzH`0Imiq-Hzm?y-e_Lw5SJlqjr7(8A5T-G@yGywp#$OH%g literal 0 HcmV?d00001 diff --git a/app/src/debug/res/drawable-xxhdpi/ic_stat_grade.png b/app/src/debug/res/drawable-xxhdpi/ic_stat_grade.png new file mode 100644 index 0000000000000000000000000000000000000000..13c26b77245c16c5038f0e6c66b47cea61e3b7e1 GIT binary patch literal 781 zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84mJh`hS0a0-5D5|u6VjQhE&{oJ9}@oh@-?Y zeYFW{QJR4&jvFpHZP+kD!As{&i%3@D!ba^Y35gDYAv%8))<#u0>Ah6y5LD4JGxQQT z81hJdfs;$~1NN@@7r)BN?AZHo@7}p5&z^g8?(>1mGUwm;)>Y4(S)0Bsq)CCJNrA&_ zy++rq6R&LwrspU8I=T7D{)zTgzILwf3V5F@{Pcga{@tY8u?Hi)|B3xvuNdCYwzwwa zoah3s%QI&`DLh`c{L9PD+76*?0qfZ(A6s*2`FjQy2Zkx~4oRw~cxu{n7#95r`}A{4 z^ZU$rhP5gGK6HjLEs9b9+4ijDuZoI;X!BPei3XRpJsb|b>-XO;aFyX%ptvm7(p56K z^h871^UPW73JhAuB(88foHg~g(|UE6Val3phAT1Fj2e;2ip;ap=H08;o_T!6v)9G( zA6GCsFt9j?F~}y|+N*G0BP`p^cp<~B6pORrPK;Z6{`d&@Sy*mTT{7zn+ox(?hxF#k zD;-IpJ_f1OqUw{@I+eR4l%8;2@HpoF zQqQn9Vlq=szqZ3)$wM%TO`=6e4M zzsj;@PR(tj1;U)Qy3%pQmwrsx$+sY4_Sq-NPl|Q7?YKR6R>P`|-^5*3Zev`cJ(ID> zVqrnUuNg7&KXpGd?BQ*ARsVGVlsC={tIqcu{`|@ia{7JLr`5G-Ul`_bM}d`}6f7=0)ef8{CX` z)#o>7RZ*X<{6yX-HzT>D;oVWcmA-0k9`LnqIG7M+usedwk*(ob>Gn;|3|}0tonxQ8 pYO*Wqm8(xgSBa`}oQnC+P?G#E*2(;04g&)NgQu&X%Q~loCIH+RWWxXe literal 0 HcmV?d00001 diff --git a/app/src/debug/res/drawable-xxhdpi/ic_stat_luckynumber.png b/app/src/debug/res/drawable-xxhdpi/ic_stat_luckynumber.png new file mode 100644 index 0000000000000000000000000000000000000000..da435745612fd0d0c7141c4b5a5ae5965d4c61a3 GIT binary patch literal 1329 zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84mJh`hS0a0-5D5Iet5b#hE&{oJ3G@O)KTR4 z{OH}0+ioUtzx}G%bn4rpNd{e~zNx%%PL+N%y=M~F5{;EE(MNKQ^5+JG#A|hzDVrG=6faopGyC^Gp)aWe>dCm^ox%>p3TZ$w-ZM2 z8TekVY7%njb`*A8%X`a=xtCFw;VjEn{xpNdj^$hauazp{GntTin{kVL$idA2JX0ic z3c?!xWgPn^J%y)>x%QgWw&n#B5*md0S6w|5X=@vpmOK0M!o}sagyrhfg-}X)=VoN^!6jW!UvB#o@Fl!>LlE1sS(8Q*N;=&;NQ{aI+@l4r?z4Tlz&6J}=MC%oVBIt6dwm>S#q!^5Dwl1zUM8kk%fh?N z(8*!9?zyMZ`Ex}WmvgT;cWn88hKCm-0>5mI{LLWcQtQmRgYR=H!z{gw!rnz6l^o4| zzf^L+I)1=XYJw!gEj?EWJIjeCY1|iFo3}k(@a~KZ7t7hi^*j!fU6*MH@J$ZUezN6Y z=_=*ozqU;4_dBvF#F%HX_p9CZj(uvsd=_qE;NAA1JR**N!=A_PhwiV@o%&WsE%>YBlBK^RKCfbpSNC^aVd!<} zh=0+#%~4JbyS>cj@TqlI{fiLX{mjGf*W>G@b9@|jHmUy!*dCoYQzJCs(f)aVR&Y=G zbzoAnC#MNK=W6$@91gpymi>6jjO@9B{H1v_r>pWhY}SYPM#vt(fMn- z*u#W>yY`>0@!R;R^*Yl6Rk6G^EFy<%*1maC{OM3Ip7fkoU*k(Y&b}-4H?vdHzh(Z7h}U7yqWc=pm_#4b>4OqOopI2qwNoJ?>G9}%Y|7}x`ILo8J z)XaM7`J8PPS*a1h%psnxr?%S_IjmXxk!ADE6B^fEooC+BFhf^1mdiibz@A|*+uQ>& oQ>K@oD9nf*y^!k)r@oxTBn^vS2f{H>0Pgg&ebxsLQ03p3^6951J literal 0 HcmV?d00001 diff --git a/app/src/debug/res/drawable-xxhdpi/ic_stat_message.png b/app/src/debug/res/drawable-xxhdpi/ic_stat_message.png new file mode 100644 index 0000000000000000000000000000000000000000..9d0fa781cb8e525fb85479e0d4e43d34cf8d62f3 GIT binary patch literal 692 zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84mJh`hS0a0-5D5|sy$sCLn>~)y|rIIE>MQ; zL-Vzy1@9FM7qBtQF5$_VuxH~+1(l;!J{HOOKfP74DxM7Uaok}aP5 zX7F2OINrXnyzt%a@FlB~`jXsCu^gf(Z?N z7rxKhwN`jVdz%TPpa5eegNyU5yw5Bm94n^FnI2;FdNW}g?+g21%(2em7bHJ^<+hp@ zvLLcVw6;0y$lues3|bc}?;O>?VEN1RSl?p*8SiYEG}b*yJF`U4f@#Hhg*nn&=Fb#4 z+;GqEa+1J><2Juf|CrN1`vuFdORfuQw}ddBn0-EDueQU^g?IiP_-DjuZ(hFS!&Ytw zZPsto17&{Q-Nw9>nL+pN?@G;E4Sk<>3$NewGh{i_mGy$p8FYgfKgG;;+V8zCzMJ`l zF#msrAKLQ2Kl7@rTmQ6NJ#KIDJJ%@=uYbNXGW;X?Nn=4Z|Mq(|9b6vLeM|xEDQCk( z9HI(E8iFeGejiO_$~?}=rE)FS>zXY$_X!Re*9GyDO0PPa86;@+DP35Z65FdaA<&}B zp)H|Khrd%_n4kPAedg*9Mj?a9!ny;qwYO+yI_zBjRr%s$rt_1O&pg%G6S&9h(|(mU zhPOW|uTL>q;9Mg3HpR0`;y^23gs35x>a{ye=2uL5CkmA;U|?Wi@O1TaS?83{1OUuw BFsA?j literal 0 HcmV?d00001 diff --git a/app/src/debug/res/drawable-xxhdpi/ic_stat_note.png b/app/src/debug/res/drawable-xxhdpi/ic_stat_note.png new file mode 100644 index 0000000000000000000000000000000000000000..64da443fd19f290efc86797da98c4909515c46c0 GIT binary patch literal 805 zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84mJh`hS0a0-5D5|-gvq=hE&{oJ0~{7KT+nm zyuPB;+j`SeZnG|3I@CIK_R*<&Ig9p{lPsiMwlpw(_+u zkF&+CWvRk4PMeO1+k)aOy(!j<=cGRH?wK`5 z=nJn-;)>-gaYxpi)O1ukF8S)_^ab;-up^CSYF0_U*StR^WxyMf0MMl-kNPJ$wCU$(gNZ zKWVTR*)>JRG>J98mlVHpa_{8KZBygZ0?wqL^Yz)kBx=LWJ-i1zo0f~Q?BQSWl;sAa zRE*V&4ab`n6h~TdZc<3KOslRgz1Da7j;H6j(hD<=es)zd7%)wjWTMuHwsj{J{E_BDtncPL`j= zS6A}gtN#1F>hr=CKNtQl4$@t=V4~nRtLt@dmo=CbFvMK=5@=;?w2x_*qxFiFpJvK( z&O38IVeRR^SMnCUFc!3L7P|8L{CD+A{|%B)!IkOU6vo^3|8sp1FCrao7R2sC;9WOv9t z;oyM%&wi*gONyIt_%%*lbP0l+XkK DmiBI= literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-anydpi-v24/ic_stat_grade.xml b/app/src/main/res/drawable-anydpi-v24/ic_stat_grade.xml index 2774dd5dd..21b406fdc 100644 --- a/app/src/main/res/drawable-anydpi-v24/ic_stat_grade.xml +++ b/app/src/main/res/drawable-anydpi-v24/ic_stat_grade.xml @@ -3,11 +3,7 @@ android:height="24dp" android:viewportWidth="28.26087" android:viewportHeight="28.26087"> - - - + diff --git a/app/src/main/res/drawable-anydpi-v24/ic_stat_luckynumber.xml b/app/src/main/res/drawable-anydpi-v24/ic_stat_luckynumber.xml index 4699c4576..bee1c7072 100644 --- a/app/src/main/res/drawable-anydpi-v24/ic_stat_luckynumber.xml +++ b/app/src/main/res/drawable-anydpi-v24/ic_stat_luckynumber.xml @@ -3,11 +3,7 @@ android:height="24dp" android:viewportWidth="26.086956" android:viewportHeight="26.086956"> - - - + diff --git a/app/src/main/res/drawable-anydpi-v24/ic_stat_message.xml b/app/src/main/res/drawable-anydpi-v24/ic_stat_message.xml index cb42d7cc2..89a8aef29 100644 --- a/app/src/main/res/drawable-anydpi-v24/ic_stat_message.xml +++ b/app/src/main/res/drawable-anydpi-v24/ic_stat_message.xml @@ -3,11 +3,7 @@ android:height="24dp" android:viewportWidth="26.086956" android:viewportHeight="26.086956"> - - - + diff --git a/app/src/main/res/drawable-anydpi-v24/ic_stat_note.xml b/app/src/main/res/drawable-anydpi-v24/ic_stat_note.xml index d6b0d4d17..0f335336b 100644 --- a/app/src/main/res/drawable-anydpi-v24/ic_stat_note.xml +++ b/app/src/main/res/drawable-anydpi-v24/ic_stat_note.xml @@ -3,11 +3,7 @@ android:height="24dp" android:viewportWidth="26.086956" android:viewportHeight="26.086956"> - - - + diff --git a/app/src/main/res/drawable/ic_main_grade.xml b/app/src/main/res/drawable/ic_main_grade.xml index 7577f399e..bf4c2cf20 100644 --- a/app/src/main/res/drawable/ic_main_grade.xml +++ b/app/src/main/res/drawable/ic_main_grade.xml @@ -1,7 +1,9 @@ - + diff --git a/app/src/main/res/drawable/ic_main_more.xml b/app/src/main/res/drawable/ic_main_more.xml index 1b436a327..f57a72456 100644 --- a/app/src/main/res/drawable/ic_main_more.xml +++ b/app/src/main/res/drawable/ic_main_more.xml @@ -1,7 +1,9 @@ - + From eb1ce251a0e8ece06086d0a29d9cd7a884e053ed Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2020 20:26:53 +0000 Subject: [PATCH 032/236] Bump threetenbp from 1.4.0 to 1.4.1 (#659) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 24a470ae0..f8fd2a3db 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -186,7 +186,7 @@ dependencies { testImplementation "junit:junit:4.13" testImplementation "io.mockk:mockk:$mockk" - testImplementation "org.threeten:threetenbp:1.4.0" + testImplementation "org.threeten:threetenbp:1.4.1" testImplementation "org.mockito:mockito-inline:3.2.4" androidTestImplementation "androidx.test:core:1.2.0" From e0ec2f8160c48c607c7f836db653a6f0c1690186 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2020 20:27:10 +0000 Subject: [PATCH 033/236] Bump firebase-core from 17.2.1 to 17.2.2 (#658) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index f8fd2a3db..612ace658 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -176,7 +176,7 @@ dependencies { implementation "com.mikepenz:aboutlibraries-core:7.1.0" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' - playImplementation "com.google.firebase:firebase-core:17.2.1" + playImplementation "com.google.firebase:firebase-core:17.2.2" playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1" releaseImplementation "fr.o80.chucker:library-no-op:$chucker" From cc242e1e87a20221771c46922e9a8b08a999498f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2020 20:27:29 +0000 Subject: [PATCH 034/236] Bump threetenabp from 1.2.1 to 1.2.2 (#657) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 612ace658..b2e495a12 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -170,7 +170,7 @@ dependencies { implementation "io.reactivex.rxjava2:rxjava:2.2.16" implementation "com.google.code.gson:gson:2.8.6" - implementation "com.jakewharton.threetenabp:threetenabp:1.2.1" + implementation "com.jakewharton.threetenabp:threetenabp:1.2.2" implementation "com.jakewharton.timber:timber:4.7.1" implementation "at.favre.lib:slf4j-timber:1.0.1" implementation "com.mikepenz:aboutlibraries-core:7.1.0" From 77e7e4e6e9591577aea2bb9e06e53df48bf511af Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2020 20:27:52 +0000 Subject: [PATCH 035/236] Bump material from 1.1.0-rc01 to 1.1.0-rc02 (#655) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index b2e495a12..18927b047 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -139,7 +139,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03" implementation "androidx.constraintlayout:constraintlayout:1.1.3" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" - implementation "com.google.android.material:material:1.1.0-rc01" + implementation "com.google.android.material:material:1.1.0-rc02" implementation "com.github.wulkanowy:material-chips-input:2.0.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "me.zhanghai.android.materialprogressbar:library:1.6.1" From c5cadbd00496cbbe119b65fe01b28011b688c371 Mon Sep 17 00:00:00 2001 From: Dominik Korsa Date: Mon, 20 Jan 2020 21:29:06 +0100 Subject: [PATCH 036/236] Improve grades dialog layout (#631) --- app/jacoco.gradle | 2 +- .../grade/details/GradeDetailsDialog.kt | 8 +- .../github/wulkanowy/utils/GradeExtension.kt | 20 +-- app/src/main/res/layout/dialog_grade.xml | 131 +++++++++--------- app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 6 files changed, 84 insertions(+), 79 deletions(-) diff --git a/app/jacoco.gradle b/app/jacoco.gradle index 48a8fed96..e9abfb613 100644 --- a/app/jacoco.gradle +++ b/app/jacoco.gradle @@ -1,7 +1,7 @@ apply plugin: "jacoco" jacoco { - toolVersion "0.8.4" + toolVersion "0.8.5" reportsDir = file("$buildDir/reports") } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt index 0abeaeea0..a8f8a8653 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsDialog.kt @@ -10,6 +10,7 @@ import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.utils.colorStringId import io.github.wulkanowy.utils.getBackgroundColor +import io.github.wulkanowy.utils.getGradeColor import io.github.wulkanowy.utils.toFormattedString import kotlinx.android.synthetic.main.dialog_grade.* @@ -50,7 +51,12 @@ class GradeDetailsDialog : DialogFragment() { super.onActivityCreated(savedInstanceState) gradeDialogSubject.text = grade.subject - gradeDialogWeightValue.text = grade.weight + + gradeDialogColorAndWeightValue.run { + text = context.getString(R.string.grade_weight_value, grade.weight) + setBackgroundResource(grade.getGradeColor()) + } + gradeDialogDateValue.text = grade.date.toFormattedString() gradeDialogColorValue.text = getString(grade.colorStringId) diff --git a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt index c1e182ba2..c57b62470 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt @@ -24,14 +24,7 @@ fun List.calcAverage(): Double { fun Grade.getBackgroundColor(theme: String): Int { return when (theme) { - "grade_color" -> when (color) { - "000000" -> R.color.grade_black - "F04C4C" -> R.color.grade_red - "20A4F7" -> R.color.grade_blue - "6ECD07" -> R.color.grade_green - "B16CF1" -> R.color.grade_purple - else -> R.color.grade_material_default - } + "grade_color" -> getGradeColor() "material" -> when (value.toInt()) { 6 -> R.color.grade_material_six 5 -> R.color.grade_material_five @@ -53,6 +46,17 @@ fun Grade.getBackgroundColor(theme: String): Int { } } +fun Grade.getGradeColor(): Int { + return when (color) { + "000000" -> R.color.grade_black + "F04C4C" -> R.color.grade_red + "20A4F7" -> R.color.grade_blue + "6ECD07" -> R.color.grade_green + "B16CF1" -> R.color.grade_purple + else -> R.color.grade_material_default + } +} + inline val Grade.colorStringId: Int get() { return when (color) { diff --git a/app/src/main/res/layout/dialog_grade.xml b/app/src/main/res/layout/dialog_grade.xml index f95b9cfdb..fad7bae57 100644 --- a/app/src/main/res/layout/dialog_grade.xml +++ b/app/src/main/res/layout/dialog_grade.xml @@ -17,90 +17,83 @@ android:padding="20dp" tools:ignore="UselessParent"> - + android:orientation="vertical"> - + + + + + + android:layout_marginRight="12dp" + android:layout_marginEnd="12dp" + android:orientation="vertical"> + - - - - - - - + + Zmień semestr Brak ocen Waga + Waga: %s Komentarz Brak nowych ocen Ilość nowych ocen: %1$d diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c8b94bff5..1b07a9fea 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -68,6 +68,7 @@ Change semester No grades Weight + Weight: %s Comment No new grades Number of new ratings: %1$d From 76a4bacb341edd02374b30a4ac557e85da0c75b9 Mon Sep 17 00:00:00 2001 From: Dominik Korsa Date: Mon, 20 Jan 2020 21:29:42 +0100 Subject: [PATCH 037/236] Fix login info autofill (#652) --- app/src/main/res/layout/fragment_login_advanced.xml | 3 ++- app/src/main/res/layout/fragment_login_form.xml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/fragment_login_advanced.xml b/app/src/main/res/layout/fragment_login_advanced.xml index 15e0904b6..7c559af9c 100644 --- a/app/src/main/res/layout/fragment_login_advanced.xml +++ b/app/src/main/res/layout/fragment_login_advanced.xml @@ -101,7 +101,7 @@ android:id="@+id/loginFormName" android:layout_width="match_parent" android:layout_height="wrap_content" - android:autofillHints="emailAddress" + android:autofillHints="username|emailAddress" android:inputType="textEmailAddress" android:maxLines="1" tools:targetApi="o" /> @@ -218,6 +218,7 @@ android:imeOptions="actionDone" android:inputType="textAutoComplete|textNoSuggestions" android:maxLines="1" + android:importantForAutofill="no" tools:ignore="LabelFor" /> diff --git a/app/src/main/res/layout/fragment_login_form.xml b/app/src/main/res/layout/fragment_login_form.xml index bb93c1827..65d331689 100644 --- a/app/src/main/res/layout/fragment_login_form.xml +++ b/app/src/main/res/layout/fragment_login_form.xml @@ -133,7 +133,7 @@ android:id="@+id/loginFormName" android:layout_width="match_parent" android:layout_height="wrap_content" - android:autofillHints="emailAddress" + android:autofillHints="username|emailAddress" android:inputType="textEmailAddress" android:maxLines="1" tools:targetApi="o" /> From e9fa95f11338070fe622181de41a457a069cc069 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2020 21:51:13 +0100 Subject: [PATCH 038/236] Bump rxjava from 2.2.16 to 2.2.17 (#656) Bumps [rxjava](https://github.com/ReactiveX/RxJava) from 2.2.16 to 2.2.17. - [Release notes](https://github.com/ReactiveX/RxJava/releases) - [Changelog](https://github.com/ReactiveX/RxJava/blob/v2.2.17/CHANGES.md) - [Commits](https://github.com/ReactiveX/RxJava/compare/v2.2.16...v2.2.17) Signed-off-by: dependabot-preview[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 18927b047..7145752d9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -167,7 +167,7 @@ dependencies { implementation "com.github.pwittchen:reactivenetwork-rx2:3.0.6" implementation "io.reactivex.rxjava2:rxandroid:2.1.1" - implementation "io.reactivex.rxjava2:rxjava:2.2.16" + implementation "io.reactivex.rxjava2:rxjava:2.2.17" implementation "com.google.code.gson:gson:2.8.6" implementation "com.jakewharton.threetenabp:threetenabp:1.2.2" From ad3bb3a52286869dc3da57c59c6118d794a41482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Mon, 20 Jan 2020 22:18:26 +0100 Subject: [PATCH 039/236] Add option to show only student plan lessons (#654) --- app/build.gradle | 2 +- .../20.json | 1634 +++++++++++++++++ .../timetable/TestTimetableEntityCreator.kt | 1 + .../github/wulkanowy/data/db/AppDatabase.kt | 6 +- .../wulkanowy/data/db/entities/Timetable.kt | 3 + .../data/db/migrations/Migration20.kt | 33 + .../preferences/PreferencesRepository.kt | 3 + .../semester/SemesterRepository.kt | 2 +- .../repositories/timetable/TimetableRemote.kt | 1 + .../ui/modules/timetable/TimetableItem.kt | 37 +- .../modules/timetable/TimetablePresenter.kt | 11 +- .../main/res/layout/item_timetable_small.xml | 81 + .../main/res/values-pl/preferences_values.xml | 6 + app/src/main/res/values-pl/strings.xml | 1 + .../main/res/values-ru/preferences_values.xml | 6 + app/src/main/res/values-ru/strings.xml | 1 + .../main/res/values/preferences_defaults.xml | 1 + app/src/main/res/values/preferences_keys.xml | 1 + .../main/res/values/preferences_values.xml | 11 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/scheme_preferences.xml | 8 + 21 files changed, 1835 insertions(+), 15 deletions(-) create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt create mode 100644 app/src/main/res/layout/item_timetable_small.xml diff --git a/app/build.gradle b/app/build.gradle index 7145752d9..0e86fc591 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -122,7 +122,7 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:0.14.2" + implementation "io.github.wulkanowy:sdk:8ad4480" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0-rc01" diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json new file mode 100644 index 000000000..925d787aa --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/20.json @@ -0,0 +1,1634 @@ +{ + "formatVersion": 1, + "database": { + "version": 20, + "identityHash": "37a216a7afcea922b66001ca5ed6cf74", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '37a216a7afcea922b66001ca5ed6cf74')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt index e2ce2255c..c1e23c578 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt @@ -21,6 +21,7 @@ fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", s teacher = teacher, teacherOld = "", info = "", + studentPlan = true, changes = changes, canceled = false ) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt index 9ee1b263d..fe4285052 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt @@ -60,6 +60,7 @@ import io.github.wulkanowy.data.db.migrations.Migration17 import io.github.wulkanowy.data.db.migrations.Migration18 import io.github.wulkanowy.data.db.migrations.Migration19 import io.github.wulkanowy.data.db.migrations.Migration2 +import io.github.wulkanowy.data.db.migrations.Migration20 import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration4 import io.github.wulkanowy.data.db.migrations.Migration5 @@ -101,7 +102,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 19 + const val VERSION_SCHEMA = 20 fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array { return arrayOf( @@ -122,7 +123,8 @@ abstract class AppDatabase : RoomDatabase() { Migration16(), Migration17(), Migration18(), - Migration19(sharedPrefProvider) + Migration19(sharedPrefProvider), + Migration20() ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt index 9bc3d2140..313feb62c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt @@ -40,6 +40,9 @@ data class Timetable( val info: String, + @ColumnInfo(name = "student_plan") + val studentPlan: Boolean, + val changes: Boolean, val canceled: Boolean diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt new file mode 100644 index 000000000..4f7cdd03f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration20.kt @@ -0,0 +1,33 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration20 : Migration(19, 20) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("DROP TABLE Timetable") + database.execSQL(""" + CREATE TABLE IF NOT EXISTS `Timetable` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `student_id` INTEGER NOT NULL, + `diary_id` INTEGER NOT NULL, + `number` INTEGER NOT NULL, + `start` INTEGER NOT NULL, + `end` INTEGER NOT NULL, + `date` INTEGER NOT NULL, + `subject` TEXT NOT NULL, + `subjectOld` TEXT NOT NULL, + `group` TEXT NOT NULL, + `room` TEXT NOT NULL, + `roomOld` TEXT NOT NULL, + `teacher` TEXT NOT NULL, + `teacherOld` TEXT NOT NULL, + `info` TEXT NOT NULL, + `student_plan` INTEGER NOT NULL, + `changes` INTEGER NOT NULL, + `canceled` INTEGER NOT NULL + ) + """) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt index 07a3654ac..523caf6c9 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/preferences/PreferencesRepository.kt @@ -65,6 +65,9 @@ class PreferencesRepository @Inject constructor( val fillMessageContent: Boolean get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content) + val showWholeClassPlan: String + get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class) + private fun getString(id: Int, default: Int) = getString(context.getString(id), default) private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt index 95fddc1b3..6377cb78e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt @@ -36,7 +36,7 @@ class SemesterRepository @Inject constructor( local.saveSemesters(new.uniqueSubtract(old)) } } else { - Timber.i("Current semesters list:\n${currentSemesters.joinToString(separator = "\n")}") + Timber.i("Current semesters list:\n${new.joinToString(separator = "\n")}") throw IllegalArgumentException("Current semester can be only one.") } }.flatMap { local.getSemesters(student).toSingle(emptyList()) }) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt index 6036bd405..26e7836ad 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt @@ -30,6 +30,7 @@ class TimetableRemote @Inject constructor(private val sdk: Sdk) { teacher = it.teacher, teacherOld = it.teacherOld, info = it.info, + studentPlan = it.studentPlan, changes = it.changes, canceled = it.canceled ) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt index 0991981df..99aca2a23 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt @@ -15,11 +15,15 @@ import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.toFormattedString import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.item_timetable.* +import kotlinx.android.synthetic.main.item_timetable_small.* -class TimetableItem(val lesson: Timetable) : +class TimetableItem(val lesson: Timetable, private val showWholeClassPlan: String) : AbstractFlexibleItem() { - override fun getLayoutRes() = R.layout.item_timetable + override fun getLayoutRes() = when { + showWholeClassPlan == "small" && !lesson.studentPlan -> R.layout.item_timetable_small + else -> R.layout.item_timetable + } override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ViewHolder { return ViewHolder(view, adapter) @@ -27,16 +31,29 @@ class TimetableItem(val lesson: Timetable) : @SuppressLint("SetTextI18n") override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList?) { - updateFields(holder) + when (itemViewType) { + R.layout.item_timetable_small -> { + with(holder) { + timetableSmallItemNumber.text = lesson.number.toString() + timetableSmallItemSubject.text = lesson.subject + timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm") + timetableSmallItemRoom.text = lesson.room + timetableSmallItemTeacher.text = lesson.teacher + } + } + R.layout.item_timetable -> { + updateFields(holder) - with(holder) { - timetableItemSubject.paintFlags = - if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG - else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() + with(holder) { + timetableItemSubject.paintFlags = + if (lesson.canceled) timetableItemSubject.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG + else timetableItemSubject.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() + } + + updateDescription(holder) + updateColors(holder) + } } - - updateDescription(holder) - updateColors(holder) } private fun updateFields(holder: ViewHolder) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt index 4c30ecb7f..d9eb44785 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -2,6 +2,8 @@ package io.github.wulkanowy.ui.modules.timetable import android.annotation.SuppressLint import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Timetable +import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository @@ -29,6 +31,7 @@ class TimetablePresenter @Inject constructor( studentRepository: StudentRepository, private val timetableRepository: TimetableRepository, private val semesterRepository: SemesterRepository, + private val prefRepository: PreferencesRepository, private val analytics: FirebaseAnalyticsHelper ) : BasePresenter(errorHandler, studentRepository, schedulers) { @@ -134,7 +137,7 @@ class TimetablePresenter @Inject constructor( .flatMap { semesterRepository.getCurrentSemester(it) } .delay(200, MILLISECONDS) .flatMap { timetableRepository.getTimetable(it, currentDate, currentDate, forceRefresh) } - .map { items -> items.map { TimetableItem(it) } } + .map { createTimetableItems(it) } .map { items -> items.sortedBy { it.lesson.number } } .subscribeOn(schedulers.backgroundThread) .observeOn(schedulers.mainThread) @@ -172,6 +175,12 @@ class TimetablePresenter @Inject constructor( } } + private fun createTimetableItems(items: List): List { + return items + .filter { if (prefRepository.showWholeClassPlan == "no") it.studentPlan else true } + .map { TimetableItem(it, prefRepository.showWholeClassPlan) } + } + private fun reloadView() { Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}") view?.apply { diff --git a/app/src/main/res/layout/item_timetable_small.xml b/app/src/main/res/layout/item_timetable_small.xml new file mode 100644 index 000000000..39e84ae07 --- /dev/null +++ b/app/src/main/res/layout/item_timetable_small.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-pl/preferences_values.xml b/app/src/main/res/values-pl/preferences_values.xml index 33a66e0f7..c3fcc9803 100644 --- a/app/src/main/res/values-pl/preferences_values.xml +++ b/app/src/main/res/values-pl/preferences_values.xml @@ -41,4 +41,10 @@ Średnia ocen z 2 semestru Średnia ocen z całego roku + + + Nie pokazuj + Pokazuj wszystkie + Pokazuj pomniejszone + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index bccd4ae18..a59146974 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -366,4 +366,5 @@ Wystąpił nieoczekiwany błąd Funkcja wyłączona przez szkołę Funkcja niedostępna w tym trybie + Pokazuj lekcje całej klasy diff --git a/app/src/main/res/values-ru/preferences_values.xml b/app/src/main/res/values-ru/preferences_values.xml index 641d754f8..0e82c2801 100644 --- a/app/src/main/res/values-ru/preferences_values.xml +++ b/app/src/main/res/values-ru/preferences_values.xml @@ -36,4 +36,10 @@ Средняя оценка со 2 семестра Средняя оценка с целого года + + + Не показывать + Показать все + Показать меньше + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0499cbf55..062386dc3 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -364,5 +364,6 @@ Произошла неожиданная ошибка Функция была выключена школой Функция не доступна в этом режиме + Показать уроки всего класса diff --git a/app/src/main/res/values/preferences_defaults.xml b/app/src/main/res/values/preferences_defaults.xml index 91b357d93..be31b440f 100644 --- a/app/src/main/res/values/preferences_defaults.xml +++ b/app/src/main/res/values/preferences_defaults.xml @@ -16,4 +16,5 @@ 0.33 0.33 true + no diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml index a2172db96..49601b550 100644 --- a/app/src/main/res/values/preferences_keys.xml +++ b/app/src/main/res/values/preferences_keys.xml @@ -16,4 +16,5 @@ grade_modifier_plus grade_modifier_minus fill_message_content + show_whole_class_plan diff --git a/app/src/main/res/values/preferences_values.xml b/app/src/main/res/values/preferences_values.xml index ee54d53c6..3b1fde65d 100644 --- a/app/src/main/res/values/preferences_values.xml +++ b/app/src/main/res/values/preferences_values.xml @@ -90,4 +90,15 @@ only_one_semester all_year + + + No show + Show all + Show smaller + + + no + yes + small + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1b07a9fea..3ad60ee2c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -301,6 +301,7 @@ Show presence in attendance Application theme Expand grades + Show whole class lessons Grades color scheme App language diff --git a/app/src/main/res/xml/scheme_preferences.xml b/app/src/main/res/xml/scheme_preferences.xml index d9e6c5333..e92aca63f 100644 --- a/app/src/main/res/xml/scheme_preferences.xml +++ b/app/src/main/res/xml/scheme_preferences.xml @@ -29,6 +29,14 @@ app:iconSpaceReserved="false" app:key="@string/pref_key_expand_grade" app:title="@string/pref_view_expand_grade" /> + Date: Wed, 22 Jan 2020 10:59:13 +0100 Subject: [PATCH 040/236] Add creators list (#636) --- app/build.gradle | 2 + app/src/main/assets/creators.json | 34 +++++++++ .../github/wulkanowy/data/RepositoryModule.kt | 5 ++ .../github/wulkanowy/data/pojos/AppCreator.kt | 3 + .../appcreator/AppCreatorRepository.kt | 20 +++++ .../ui/modules/about/AboutFragment.kt | 10 +++ .../ui/modules/about/AboutPresenter.kt | 6 ++ .../wulkanowy/ui/modules/about/AboutView.kt | 4 + .../modules/about/creator/CreatorFragment.kt | 76 +++++++++++++++++++ .../ui/modules/about/creator/CreatorItem.kt | 50 ++++++++++++ .../modules/about/creator/CreatorPresenter.kt | 43 +++++++++++ .../ui/modules/about/creator/CreatorView.kt | 16 ++++ .../wulkanowy/ui/modules/main/MainModule.kt | 5 ++ .../main/res/drawable/ic_about_creator.xml | 9 +++ app/src/main/res/layout/fragment_creator.xml | 37 +++++++++ app/src/main/res/layout/item_creator.xml | 34 +++++++++ app/src/main/res/values-pl/strings.xml | 8 ++ app/src/main/res/values/strings.xml | 8 ++ 18 files changed, 370 insertions(+) create mode 100644 app/src/main/assets/creators.json create mode 100644 app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorFragment.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorItem.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorPresenter.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorView.kt create mode 100644 app/src/main/res/drawable/ic_about_creator.xml create mode 100644 app/src/main/res/layout/fragment_creator.xml create mode 100644 app/src/main/res/layout/item_creator.xml diff --git a/app/build.gradle b/app/build.gradle index 0e86fc591..e17731d40 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -176,6 +176,8 @@ dependencies { implementation "com.mikepenz:aboutlibraries-core:7.1.0" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' + implementation("io.coil-kt:coil:0.9.2") + playImplementation "com.google.firebase:firebase-core:17.2.2" playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1" diff --git a/app/src/main/assets/creators.json b/app/src/main/assets/creators.json new file mode 100644 index 000000000..b99f51e21 --- /dev/null +++ b/app/src/main/assets/creators.json @@ -0,0 +1,34 @@ +[ + { + "displayName": "Mikołaj Pich", + "githubUsername": "mklkj" + }, + { + "displayName": "Rafał Borcz", + "githubUsername": "Faierbel" + }, + { + "displayName": "Dominik Korsa", + "githubUsername": "dominik-korsa" + }, + { + "displayName": "Kacper Ziubryniewicz", + "githubUsername": "kapi2289" + }, + { + "displayName": "doteq", + "githubUsername": "doteq" + }, + { + "displayName": "Pavuloff", + "githubUsername": "pavuloff" + }, + { + "displayName": "Piotr Romanowski", + "githubUsername": "v0idzz" + }, + { + "displayName": "Dinolek", + "githubUsername": "Dinolek" + } +] diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt index 1bc4eb616..48e51e3ce 100644 --- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.data import android.content.Context import android.content.SharedPreferences +import android.content.res.AssetManager import android.content.res.Resources import androidx.preference.PreferenceManager import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings @@ -58,6 +59,10 @@ internal class RepositoryModule { @Provides fun provideResources(context: Context): Resources = context.resources + @Singleton + @Provides + fun provideAssets(context: Context): AssetManager = context.assets + @Singleton @Provides fun provideSharedPref(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt new file mode 100644 index 000000000..d67aa2a7f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/pojos/AppCreator.kt @@ -0,0 +1,3 @@ +package io.github.wulkanowy.data.pojos + +class AppCreator(val displayName: String, val githubUsername: String) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt new file mode 100644 index 000000000..095b37166 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/appcreator/AppCreatorRepository.kt @@ -0,0 +1,20 @@ +package io.github.wulkanowy.data.repositories.appcreator + +import android.content.res.AssetManager +import com.google.gson.Gson +import io.github.wulkanowy.data.pojos.AppCreator +import io.reactivex.Single +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AppCreatorRepository @Inject constructor(private val assets: AssetManager) { + fun getAppCreators(): Single> { + return Single.fromCallable> { + Gson().fromJson( + assets.open("creators.json").bufferedReader().use { it.readText() }, + Array::class.java + ).toList() + } + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt index 22276bdf9..d9430a47a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutFragment.kt @@ -16,6 +16,7 @@ import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import io.github.wulkanowy.R import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment import io.github.wulkanowy.ui.modules.about.license.LicenseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView @@ -42,6 +43,11 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { Triple(getString(R.string.about_version), "${appInfo.versionName} (${appInfo.versionCode})", getCompatDrawable(R.drawable.ic_all_about)) } + override val creatorsRes: Triple? + get() = context?.run { + Triple(getString(R.string.about_creator), getString(R.string.about_creator_summary), getCompatDrawable(R.drawable.ic_about_creator)) + } + override val feedbackRes: Triple? get() = context?.run { Triple(getString(R.string.about_feedback), getString(R.string.about_feedback_summary), getCompatDrawable(R.drawable.ic_about_feedback)) @@ -143,6 +149,10 @@ class AboutFragment : BaseFragment(), AboutView, MainView.TitledView { (activity as? MainActivity)?.pushView(LicenseFragment.newInstance()) } + override fun openCreators() { + (activity as? MainActivity)?.pushView(CreatorFragment.newInstance()) + } + override fun openPrivacyPolicy() { context?.openInternetBrowser("https://wulkanowy.github.io/polityka-prywatnosci.html", ::showMessage) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt index 8087c9556..662939832 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt @@ -52,6 +52,11 @@ class AboutPresenter @Inject constructor( openLicenses() analytics.logEvent("about_open", "name" to "licenses") } + creatorsRes?.first -> { + Timber.i("Opening creators view") + openCreators() + analytics.logEvent("about_open", "name" to "creators") + } privacyRes?.first -> { Timber.i("Opening privacy page ") openPrivacyPolicy() @@ -65,6 +70,7 @@ class AboutPresenter @Inject constructor( view?.run { updateData(AboutScrollableHeader(), listOfNotNull( versionRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, + creatorsRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, feedbackRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, faqRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, discordRes?.let { (title, summary, image) -> AboutItem(title, summary, image) }, diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt index 34850bae4..228225ec6 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt @@ -7,6 +7,8 @@ interface AboutView : BaseView { val versionRes: Triple? + val creatorsRes: Triple? + val feedbackRes: Triple? val faqRes: Triple? @@ -33,5 +35,7 @@ interface AboutView : BaseView { fun openLicenses() + fun openCreators() + fun openPrivacyPolicy() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorFragment.kt new file mode 100644 index 000000000..299657ba6 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorFragment.kt @@ -0,0 +1,76 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.common.FlexibleItemDecoration +import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.R +import io.github.wulkanowy.ui.base.BaseFragment +import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.openInternetBrowser +import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.fragment_creator.* +import javax.inject.Inject + +class CreatorFragment : BaseFragment(), CreatorView, MainView.TitledView { + + @Inject + lateinit var presenter: CreatorPresenter + + @Inject + lateinit var creatorsAdapter: FlexibleAdapter> + + override val titleStringId get() = R.string.creators_title + + companion object { + fun newInstance() = CreatorFragment() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.fragment_creator, container, false) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + presenter.onAttachView(this) + } + + override fun initView() { + with(creatorRecycler) { + layoutManager = SmoothScrollLinearLayoutManager(context) + adapter = creatorsAdapter + addItemDecoration(FlexibleItemDecoration(context) + .withDefaultDivider() + .withDrawDividerOnLastItem(false)) + } + creatorsAdapter.setOnItemClickListener(presenter::onItemSelected) + creatorSeeMore.setOnClickListener { presenter.onSeeMoreClick() } + } + + override fun updateData(data: List) { + creatorsAdapter.updateDataSet(data) + } + + override fun openUserGithubPage(username: String) { + context?.openInternetBrowser("https://github.com/${username}", ::showMessage) + } + + override fun openGithubContributorsPage() { + context?.openInternetBrowser("https://github.com/wulkanowy/wulkanowy/graphs/contributors", ::showMessage) + } + + override fun showProgress(show: Boolean) { + creatorProgress.visibility = if (show) VISIBLE else GONE + } + + override fun onDestroyView() { + presenter.onDetachView() + super.onDestroyView() + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorItem.kt new file mode 100644 index 000000000..909919bbc --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorItem.kt @@ -0,0 +1,50 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import android.view.View +import coil.api.load +import coil.transform.RoundedCornersTransformation +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.viewholders.FlexibleViewHolder +import io.github.wulkanowy.R +import io.github.wulkanowy.data.pojos.AppCreator +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.item_creator.* + +class CreatorItem(val creator: AppCreator) : AbstractFlexibleItem() { + + override fun getLayoutRes() = R.layout.item_creator + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>) = ViewHolder(view, adapter) + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ViewHolder, position: Int, payloads: MutableList) { + with(holder) { + creatorItemName.text = creator.displayName + + creatorItemAvatar.load("https://github.com/${creator.githubUsername}.png") { + transformations(RoundedCornersTransformation(8f)) + crossfade(true) + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as CreatorItem + + if (creator != other.creator) return false + + return true + } + + override fun hashCode() = creator.hashCode() + + class ViewHolder(view: View, adapter: FlexibleAdapter>) : FlexibleViewHolder(view, adapter), + LayoutContainer { + + override val containerView: View? get() = contentView + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorPresenter.kt new file mode 100644 index 000000000..bc8034ab5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorPresenter.kt @@ -0,0 +1,43 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.pojos.AppCreator +import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository +import io.github.wulkanowy.data.repositories.student.StudentRepository +import io.github.wulkanowy.ui.base.BasePresenter +import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.SchedulersProvider +import io.reactivex.Single +import javax.inject.Inject + +class CreatorPresenter @Inject constructor( + schedulers: SchedulersProvider, + errorHandler: ErrorHandler, + studentRepository: StudentRepository, + private val appCreatorRepository: AppCreatorRepository +) : BasePresenter(errorHandler, studentRepository, schedulers) { + + override fun onAttachView(view: CreatorView) { + super.onAttachView(view) + view.initView() + loadData() + } + + fun onItemSelected(item: AbstractFlexibleItem<*>) { + if (item !is CreatorItem) return + view?.openUserGithubPage(item.creator.githubUsername) + } + + fun onSeeMoreClick() { + view?.openGithubContributorsPage() + } + + private fun loadData() { + disposable.add(appCreatorRepository.getAppCreators() + .map { it.map { creator -> CreatorItem(creator) } } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { view?.showProgress(false) } + .subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) })) + } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorView.kt new file mode 100644 index 000000000..2a6a43039 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/creator/CreatorView.kt @@ -0,0 +1,16 @@ +package io.github.wulkanowy.ui.modules.about.creator + +import io.github.wulkanowy.ui.base.BaseView + +interface CreatorView : BaseView { + + fun initView() + + fun updateData(data: List) + + fun openUserGithubPage(username: String) + + fun openGithubContributorsPage() + + fun showProgress(show: Boolean) +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt index bbaea51ce..41f4d5f2c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt @@ -8,6 +8,7 @@ import dagger.android.ContributesAndroidInjector import io.github.wulkanowy.R import io.github.wulkanowy.di.scopes.PerFragment import io.github.wulkanowy.ui.modules.about.AboutFragment +import io.github.wulkanowy.ui.modules.about.creator.CreatorFragment import io.github.wulkanowy.ui.modules.about.license.LicenseFragment import io.github.wulkanowy.ui.modules.about.license.LicenseModule import io.github.wulkanowy.ui.modules.account.AccountDialog @@ -121,6 +122,10 @@ abstract class MainModule { @ContributesAndroidInjector(modules = [LicenseModule::class]) abstract fun bindLicenseFragment(): LicenseFragment + @PerFragment + @ContributesAndroidInjector() + abstract fun bindCreatorsFragment(): CreatorFragment + @PerFragment @ContributesAndroidInjector(modules = [SchoolAndTeachersModule::class]) abstract fun bindSchoolAndTeachersFragment(): SchoolAndTeachersFragment diff --git a/app/src/main/res/drawable/ic_about_creator.xml b/app/src/main/res/drawable/ic_about_creator.xml new file mode 100644 index 000000000..c3daf609d --- /dev/null +++ b/app/src/main/res/drawable/ic_about_creator.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/fragment_creator.xml b/app/src/main/res/layout/fragment_creator.xml new file mode 100644 index 000000000..a0f34cffe --- /dev/null +++ b/app/src/main/res/layout/fragment_creator.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_creator.xml b/app/src/main/res/layout/item_creator.xml new file mode 100644 index 000000000..1a8d0285e --- /dev/null +++ b/app/src/main/res/layout/item_creator.xml @@ -0,0 +1,34 @@ + + + + + + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index a59146974..458f64b45 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -12,6 +12,7 @@ Ustawienia Więcej O aplikacji + Twórcy Licencje Wiadomości Nowa wiadomość @@ -269,6 +270,8 @@ Wersja aplikacji + Twórcy + Lista programistów Wulkanowego Zgłoś błąd Wyślij zgłoszenie o błędzie poprzez e-maila FAQ @@ -287,6 +290,11 @@ Licencja + + Awatar + Zobacz więcej na GitHub + + Treść Ponów diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3ad60ee2c..8bdc54105 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ Settings More About + Creators Licenses Messages New message @@ -250,6 +251,8 @@ App version + Creators + List of Wulkanowy developers Report a bug Send a bug report via e-mail FAQ @@ -268,6 +271,11 @@ License + + Avatar + See more on GitHub + + Content Retry From 7142e05561185377490964d5adf0706dc85d60b3 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2020 08:58:31 +0000 Subject: [PATCH 041/236] Bump work_manager from 2.3.0-rc01 to 2.3.0 (#660) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index e17731d40..5ae33f714 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -110,7 +110,7 @@ play { } ext { - work_manager = "2.3.0-rc01" + work_manager = "2.3.0" room = "2.2.3" dagger = "2.25.4" chucker = "2.0.4" From 1dbe47039188abf68752780fbf086520bce73dae Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2020 08:59:20 +0000 Subject: [PATCH 042/236] Bump activity-ktx from 1.1.0-rc03 to 1.1.0 (#661) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 5ae33f714..3c573ac3c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -126,7 +126,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0-rc01" - implementation "androidx.activity:activity-ktx:1.1.0-rc03" + implementation "androidx.activity:activity-ktx:1.1.0" implementation "androidx.appcompat:appcompat:1.1.0" implementation "androidx.appcompat:appcompat-resources:1.1.0" implementation "androidx.fragment:fragment-ktx:1.2.0-rc05" From 0c5d45717cfd20aed61ce56c0486133600909778 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2020 09:18:54 +0000 Subject: [PATCH 043/236] Bump fragment-ktx from 1.2.0-rc05 to 1.2.0 (#662) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 3c573ac3c..819ed4f4c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -129,7 +129,7 @@ dependencies { implementation "androidx.activity:activity-ktx:1.1.0" implementation "androidx.appcompat:appcompat:1.1.0" implementation "androidx.appcompat:appcompat-resources:1.1.0" - implementation "androidx.fragment:fragment-ktx:1.2.0-rc05" + implementation "androidx.fragment:fragment-ktx:1.2.0" implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.multidex:multidex:2.0.1" From 19f495cba6422e3d845db85acc500545e17beced Mon Sep 17 00:00:00 2001 From: Kacper Ziubryniewicz Date: Sat, 25 Jan 2020 18:07:25 +0100 Subject: [PATCH 044/236] Add excusing for absence (#533) --- .../21.json | 1652 +++++++++++++++++ .../attendance/AttendanceLocalTest.kt | 6 +- .../timetable/TestTimetableEntityCreator.kt | 2 +- .../github/wulkanowy/data/db/AppDatabase.kt | 6 +- .../wulkanowy/data/db/entities/Attendance.kt | 11 +- .../data/db/migrations/Migration21.kt | 13 + .../attendance/AttendanceRemote.kt | 17 +- .../attendance/AttendanceRepository.kt | 4 + .../attendance/SentExcuseStatus.kt | 7 + .../modules/attendance/AttendanceAdapter.kt | 12 + .../modules/attendance/AttendanceFragment.kt | 80 +- .../ui/modules/attendance/AttendanceItem.kt | 48 +- .../ui/modules/attendance/AttendanceModule.kt | 12 + .../modules/attendance/AttendancePresenter.kt | 96 +- .../ui/modules/attendance/AttendanceView.kt | 16 + .../wulkanowy/ui/modules/main/MainActivity.kt | 6 + .../wulkanowy/ui/modules/main/MainModule.kt | 3 +- .../ui/modules/main/MainPresenter.kt | 1 + .../wulkanowy/ui/modules/main/MainView.kt | 4 + .../drawable-night/background_header_note.xml | 4 +- .../res/drawable/background_header_note.xml | 6 +- app/src/main/res/drawable/ic_all_done.xml | 10 + app/src/main/res/drawable/ic_all_done_all.xml | 10 + .../main/res/drawable/ic_excuse_denied.xml | 10 + .../main/res/drawable/ic_excuse_waiting.xml | 10 + app/src/main/res/layout/dialog_excuse.xml | 21 + app/src/main/res/layout/dialog_grade.xml | 21 +- .../main/res/layout/fragment_attendance.xml | 14 + .../main/res/layout/header_grade_details.xml | 11 +- app/src/main/res/layout/item_attendance.xml | 44 +- app/src/main/res/menu/context_menu_excuse.xml | 11 + app/src/main/res/values-pl/strings.xml | 5 + app/src/main/res/values/strings.xml | 5 + app/src/main/res/values/styles.xml | 1 + 34 files changed, 2132 insertions(+), 47 deletions(-) create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/attendance/SentExcuseStatus.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceModule.kt create mode 100644 app/src/main/res/drawable/ic_all_done.xml create mode 100644 app/src/main/res/drawable/ic_all_done_all.xml create mode 100644 app/src/main/res/drawable/ic_excuse_denied.xml create mode 100644 app/src/main/res/drawable/ic_excuse_waiting.xml create mode 100644 app/src/main/res/layout/dialog_excuse.xml create mode 100644 app/src/main/res/menu/context_menu_excuse.xml diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json new file mode 100644 index 000000000..dfad69f5d --- /dev/null +++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/21.json @@ -0,0 +1,1652 @@ +{ + "formatVersion": 1, + "database": { + "version": 21, + "identityHash": "f905d8a3ff4718d30ae7413a5a717b1f", + "entities": [ + { + "tableName": "Students", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scrapperBaseUrl", + "columnName": "scrapper_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mobileBaseUrl", + "columnName": "mobile_base_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginType", + "columnName": "login_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginMode", + "columnName": "login_mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "certificateKey", + "columnName": "certificate_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "privateKey", + "columnName": "private_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isParent", + "columnName": "is_parent", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "symbol", + "columnName": "symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userLoginId", + "columnName": "user_login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentName", + "columnName": "student_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolSymbol", + "columnName": "school_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolName", + "columnName": "school_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "className", + "columnName": "class_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "registrationDate", + "columnName": "registration_date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Students_email_symbol_student_id_school_id_class_id", + "unique": true, + "columnNames": [ + "email", + "symbol", + "student_id", + "school_id", + "class_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Semesters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryName", + "columnName": "diary_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "schoolYear", + "columnName": "school_year", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterName", + "columnName": "semester_name", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isCurrent", + "columnName": "is_current", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_Semesters_student_id_diary_id_semester_id", + "unique": true, + "columnNames": [ + "student_id", + "diary_id", + "semester_id" + ], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Exams", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Timetable", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subjectOld", + "columnName": "subjectOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "room", + "columnName": "room", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roomOld", + "columnName": "roomOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherOld", + "columnName": "teacherOld", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "info", + "columnName": "info", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "studentPlan", + "columnName": "student_plan", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "changes", + "columnName": "changes", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "canceled", + "columnName": "canceled", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Attendance", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timeId", + "columnName": "time_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excused", + "columnName": "excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excusable", + "columnName": "excusable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "excuseStatus", + "columnName": "excuse_status", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "AttendanceSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subjectId", + "columnName": "subject_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "month", + "columnName": "month", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "presence", + "columnName": "presence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceExcused", + "columnName": "absence_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absenceForSchoolReasons", + "columnName": "absence_for_school_reasons", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lateness", + "columnName": "lateness", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latenessExcused", + "columnName": "lateness_excused", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exemption", + "columnName": "exemption", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Grades", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "entry", + "columnName": "entry", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "modifier", + "columnName": "modifier", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "gradeSymbol", + "columnName": "grade_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weightValue", + "columnName": "weightValue", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesSummary", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "predictedGrade", + "columnName": "predicted_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalGrade", + "columnName": "final_grade", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proposedPoints", + "columnName": "proposed_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "finalPoints", + "columnName": "final_points", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pointsSum", + "columnName": "points_sum", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "average", + "columnName": "average", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "amount", + "columnName": "amount", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "is_semester", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "GradesPointsStatistics", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "others", + "columnName": "others", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "student", + "columnName": "student", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Messages", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `removed` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "message_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sender", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "recipient", + "columnName": "recipient_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unread", + "columnName": "unread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unreadBy", + "columnName": "unread_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "readBy", + "columnName": "read_by", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "removed", + "columnName": "removed", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Notes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `category` TEXT NOT NULL, `content` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isRead", + "columnName": "is_read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Homework", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "semesterId", + "columnName": "semester_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "entryDate", + "columnName": "entry_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Subjects", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LuckyNumbers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotified", + "columnName": "is_notified", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "luckyNumber", + "columnName": "lucky_number", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "CompletedLesson", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "diaryId", + "columnName": "diary_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "number", + "columnName": "number", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "topic", + "columnName": "topic", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacher", + "columnName": "teacher", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "teacherSymbol", + "columnName": "teacher_symbol", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "substitution", + "columnName": "substitution", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absence", + "columnName": "absence", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resources", + "columnName": "resources", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "ReportingUnits", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "senderId", + "columnName": "sender_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "senderName", + "columnName": "sender_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "roles", + "columnName": "roles", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Recipients", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "realId", + "columnName": "real_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "realName", + "columnName": "real_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "loginId", + "columnName": "login_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "unitId", + "columnName": "unit_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "role", + "columnName": "role", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hash", + "columnName": "hash", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "MobileDevices", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "device_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Teachers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "subject", + "columnName": "subject", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "School", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "studentId", + "columnName": "student_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "classId", + "columnName": "class_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contact", + "columnName": "contact", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "headmaster", + "columnName": "headmaster", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pedagogue", + "columnName": "pedagogue", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f905d8a3ff4718d30ae7413a5a717b1f')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt index 69502e742..5fd5b193a 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/attendance/AttendanceLocalTest.kt @@ -35,9 +35,9 @@ class AttendanceLocalTest { @Test fun saveAndReadTest() { attendanceLocal.saveAttendance(listOf( - Attendance(1, 2, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false), - Attendance(1, 2, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false), - Attendance(1, 2, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false) + Attendance(1, 2, 3, LocalDate.of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name), + Attendance(1, 2, 3, LocalDate.of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name), + Attendance(1, 2, 3, LocalDate.of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name) )) val attendance = attendanceLocal diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt index c1e23c578..77c061821 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/timetable/TestTimetableEntityCreator.kt @@ -2,8 +2,8 @@ package io.github.wulkanowy.data.repositories.timetable import org.threeten.bp.LocalDateTime import org.threeten.bp.LocalDateTime.now -import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal +import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote fun createTimetableLocal(start: LocalDateTime, number: Int, room: String = "", subject: String = "", teacher: String = "", changes: Boolean = false): TimetableLocal { return TimetableLocal( 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 fe4285052..b5293a53c 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 @@ -61,6 +61,7 @@ import io.github.wulkanowy.data.db.migrations.Migration18 import io.github.wulkanowy.data.db.migrations.Migration19 import io.github.wulkanowy.data.db.migrations.Migration2 import io.github.wulkanowy.data.db.migrations.Migration20 +import io.github.wulkanowy.data.db.migrations.Migration21 import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration4 import io.github.wulkanowy.data.db.migrations.Migration5 @@ -102,7 +103,7 @@ import javax.inject.Singleton abstract class AppDatabase : RoomDatabase() { companion object { - const val VERSION_SCHEMA = 20 + const val VERSION_SCHEMA = 21 fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array { return arrayOf( @@ -124,7 +125,8 @@ abstract class AppDatabase : RoomDatabase() { Migration17(), Migration18(), Migration19(sharedPrefProvider), - Migration20() + Migration20(), + Migration21() ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt index 3c58971ae..aa8da8db7 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Attendance.kt @@ -15,6 +15,9 @@ data class Attendance( @ColumnInfo(name = "diary_id") val diaryId: Int, + @ColumnInfo(name = "time_id") + val timeId: Int, + val date: LocalDate, val number: Int, @@ -33,7 +36,13 @@ data class Attendance( val excused: Boolean, - val deleted: Boolean + val deleted: Boolean, + + val excusable: Boolean, + + @ColumnInfo(name = "excuse_status") + val excuseStatus: String? + ) : Serializable { @PrimaryKey(autoGenerate = true) diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt new file mode 100644 index 000000000..9d95f2ba4 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration21.kt @@ -0,0 +1,13 @@ +package io.github.wulkanowy.data.db.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration21 : Migration(20, 21) { + + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE Attendance ADD COLUMN excusable INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Attendance ADD COLUMN time_id INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE Attendance ADD COLUMN excuse_status TEXT DEFAULT NULL") + } +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt index cd7702c10..1828c2bdf 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRemote.kt @@ -3,8 +3,11 @@ package io.github.wulkanowy.data.repositories.attendance import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.sdk.pojo.Absent import io.reactivex.Single import org.threeten.bp.LocalDate +import org.threeten.bp.LocalDateTime +import org.threeten.bp.LocalTime import javax.inject.Inject import javax.inject.Singleton @@ -19,6 +22,7 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) { studentId = semester.studentId, diaryId = semester.diaryId, date = it.date, + timeId = it.timeId, number = it.number, subject = it.subject, name = it.name, @@ -27,9 +31,20 @@ class AttendanceRemote @Inject constructor(private val sdk: Sdk) { exemption = it.exemption, lateness = it.lateness, excused = it.excused, - deleted = it.deleted + deleted = it.deleted, + excusable = it.excusable, + excuseStatus = it.excuseStatus?.name ) } } } + + fun excuseAbsence(semester: Semester, absenceList: List, reason: String?): Single { + return sdk.switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance -> + Absent( + date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)), + timeId = attendance.timeId + ) + }, reason) + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt index 85102b3c5..fc3830c2e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/AttendanceRepository.kt @@ -41,4 +41,8 @@ class AttendanceRepository @Inject constructor( }).map { list -> list.filter { it.date in startDate..endDate } } } } + + fun excuseForAbsence(semester: Semester, attendanceList: List, reason: String? = null): Single { + return remote.excuseAbsence(semester, attendanceList, reason) + } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/SentExcuseStatus.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/SentExcuseStatus.kt new file mode 100644 index 000000000..50d6b8ed5 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/attendance/SentExcuseStatus.kt @@ -0,0 +1,7 @@ +package io.github.wulkanowy.data.repositories.attendance + +enum class SentExcuseStatus(val id: Int = 0) { + WAITING, + ACCEPTED, + DENIED +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt new file mode 100644 index 000000000..75f998404 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.ui.modules.attendance + +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.IFlexible +import io.github.wulkanowy.data.db.entities.Attendance + +class AttendanceAdapter> : FlexibleAdapter(null, null, true) { + + var excuseActionMode: Boolean = false + + var onExcuseCheckboxSelect: (attendanceItem: Attendance, checked: Boolean) -> Unit = { _, _ -> } +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt index bc7c1cacd..9969b1c78 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt @@ -1,5 +1,6 @@ package io.github.wulkanowy.ui.modules.attendance +import android.content.DialogInterface.BUTTON_POSITIVE import android.os.Bundle import android.view.LayoutInflater import android.view.Menu @@ -10,8 +11,9 @@ import android.view.View.GONE import android.view.View.INVISIBLE import android.view.View.VISIBLE import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.view.ActionMode import com.wdullaer.materialdatetimepicker.date.DatePickerDialog -import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.FlexibleItemDecoration import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -24,6 +26,7 @@ import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.utils.SchooldaysRangeLimiter import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.setOnItemClickListener +import kotlinx.android.synthetic.main.dialog_excuse.* import kotlinx.android.synthetic.main.fragment_attendance.* import org.threeten.bp.LocalDate import javax.inject.Inject @@ -35,7 +38,13 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie lateinit var presenter: AttendancePresenter @Inject - lateinit var attendanceAdapter: FlexibleAdapter> + lateinit var attendanceAdapter: AttendanceAdapter> + + override val excuseSuccessString: String + get() = getString(R.string.attendance_excuse_success) + + override val excuseNoSelectionString: String + get() = getString(R.string.attendance_excuse_no_selection) companion object { private const val SAVED_DATE_KEY = "CURRENT_DATE" @@ -49,6 +58,34 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize + override val excuseActionMode: Boolean get() = attendanceAdapter.excuseActionMode + + private var actionMode: ActionMode? = null + private val actionModeCallback = object : ActionMode.Callback { + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + val inflater = mode.menuInflater + inflater.inflate(R.menu.context_menu_excuse, menu) + return true + } + + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { + mode.title = getString(R.string.attendance_excuse_title) + return presenter.onPrepareActionMode() + } + + override fun onDestroyActionMode(mode: ActionMode) { + presenter.onDestroyActionMode() + actionMode = null + } + + override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean { + return when (menu.itemId) { + R.id.excuseMenuSubmit -> presenter.onExcuseSubmitButtonClick() + else -> false + } + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -66,6 +103,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie override fun initView() { attendanceAdapter.setOnItemClickListener(presenter::onAttendanceItemSelected) + attendanceAdapter.onExcuseCheckboxSelect = presenter::onExcuseCheckboxSelect with(attendanceRecycler) { layoutManager = SmoothScrollLinearLayoutManager(context) @@ -83,6 +121,8 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie attendanceNavDate.setOnClickListener { presenter.onPickDate() } attendanceNextButton.setOnClickListener { presenter.onNextDay() } + attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() } + attendanceNavContainer.setElevationCompat(requireContext().dpToPx(8f)) } @@ -115,6 +155,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie if (::presenter.isInitialized) presenter.onViewReselected() } + override fun onFragmentChanged() { + if (::presenter.isInitialized) presenter.onMainViewChanged() + } + override fun popView() { (activity as? MainActivity)?.popView() } @@ -155,6 +199,10 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie attendanceNextButton.visibility = if (show) VISIBLE else INVISIBLE } + override fun showExcuseButton(show: Boolean) { + attendanceExcuseButton.visibility = if (show) VISIBLE else GONE + } + override fun showAttendanceDialog(lesson: Attendance) { (activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson)) } @@ -174,10 +222,38 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie } } + override fun showExcuseDialog() { + AlertDialog.Builder(requireContext()) + .setTitle(R.string.attendance_excuse_title) + .setView(R.layout.dialog_excuse) + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .create() + .apply { + setButton(BUTTON_POSITIVE, getString(R.string.attendance_excuse_dialog_submit)) { _, _ -> + presenter.onExcuseDialogSubmit(excuseReason.text?.toString().orEmpty()) + } + }.show() + } + override fun openSummaryView() { (activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance()) } + override fun startActionMode() { + actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback) + } + + override fun showExcuseCheckboxes(show: Boolean) { + attendanceAdapter.apply { + excuseActionMode = show + notifyDataSetChanged() + } + } + + override fun finishActionMode() { + actionMode?.finish() + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt index 16a140cbf..7355aec2e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceItem.kt @@ -1,18 +1,22 @@ package io.github.wulkanowy.ui.modules.attendance import android.view.View +import android.view.View.GONE import android.view.View.INVISIBLE import android.view.View.VISIBLE +import androidx.core.view.isVisible import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.viewholders.FlexibleViewHolder import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Attendance +import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.item_attendance.* -class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem() { +class AttendanceItem(val attendance: Attendance) : + AbstractFlexibleItem() { override fun getLayoutRes() = R.layout.item_attendance @@ -26,6 +30,34 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem + (adapter as AttendanceAdapter).onExcuseCheckboxSelect(attendance, checked) + } + + when (if (attendance.excuseStatus != null) SentExcuseStatus.valueOf(attendance.excuseStatus) else null) { + SentExcuseStatus.WAITING -> { + attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_waiting) + attendanceItemExcuseInfo.visibility = VISIBLE + attendanceItemAlert.visibility = INVISIBLE + } + SentExcuseStatus.DENIED -> { + attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_denied) + attendanceItemExcuseInfo.visibility = VISIBLE + } + else -> { + if (attendance.excusable && (adapter as AttendanceAdapter).excuseActionMode) { + attendanceItemNumber.visibility = GONE + attendanceItemExcuseCheckbox.visibility = VISIBLE + } else { + attendanceItemNumber.visibility = VISIBLE + attendanceItemExcuseCheckbox.visibility = GONE + } + } + } } } @@ -46,8 +78,20 @@ class AttendanceItem(val attendance: Attendance) : AbstractFlexibleItem) : FlexibleViewHolder(view, adapter), LayoutContainer { + class ViewHolder(view: View, val adapter: FlexibleAdapter<*>) : + FlexibleViewHolder(view, adapter), + LayoutContainer { + override val containerView: View get() = contentView + + override fun onClick(view: View?) { + super.onClick(view) + attendanceItemExcuseCheckbox.apply { + if ((adapter as AttendanceAdapter).excuseActionMode && isVisible) { + isChecked = !isChecked + } + } + } } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceModule.kt new file mode 100644 index 000000000..eb35fea1b --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceModule.kt @@ -0,0 +1,12 @@ +package io.github.wulkanowy.ui.modules.attendance + +import dagger.Module +import dagger.Provides +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem + +@Module +class AttendanceModule { + + @Provides + fun provideAttendanceFlexibleAdapter() = AttendanceAdapter>() +} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index 1490ee6e3..679d5367d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.attendance import android.annotation.SuppressLint import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository @@ -40,6 +41,8 @@ class AttendancePresenter @Inject constructor( private lateinit var lastError: Throwable + private val attendanceToExcuseList = mutableListOf() + fun onAttachView(view: AttendanceView, date: Long?) { super.onAttachView(view) view.initView() @@ -51,11 +54,15 @@ class AttendancePresenter @Inject constructor( } fun onPreviousDay() { + view?.finishActionMode() + attendanceToExcuseList.clear() loadData(currentDate.previousSchoolDay) reloadView() } fun onNextDay() { + view?.finishActionMode() + attendanceToExcuseList.clear() loadData(currentDate.nextSchoolDay) reloadView() } @@ -100,10 +107,59 @@ class AttendancePresenter @Inject constructor( } } + fun onMainViewChanged() { + view?.finishActionMode() + } + fun onAttendanceItemSelected(item: AbstractFlexibleItem<*>?) { - if (item is AttendanceItem) { - Timber.i("Select attendance item ${item.attendance.id}") - view?.showAttendanceDialog(item.attendance) + view?.apply { + if (item is AttendanceItem && !excuseActionMode) { + Timber.i("Select attendance item ${item.attendance.id}") + showAttendanceDialog(item.attendance) + } + } + } + + fun onExcuseButtonClick() { + view?.startActionMode() + } + + fun onExcuseCheckboxSelect(attendanceItem: Attendance, checked: Boolean) { + if (checked) attendanceToExcuseList.add(attendanceItem) + else attendanceToExcuseList.remove(attendanceItem) + } + + fun onExcuseSubmitButtonClick(): Boolean { + view?.apply { + return if (attendanceToExcuseList.isNotEmpty()) { + showExcuseDialog() + true + } else { + showMessage(excuseNoSelectionString) + false + } + } + return false + } + + fun onExcuseDialogSubmit(reason: String) { + view?.finishActionMode() + excuseAbsence(if (reason != "") reason else null, attendanceToExcuseList.toList()) + } + + fun onPrepareActionMode(): Boolean { + view?.apply { + showExcuseCheckboxes(true) + showExcuseButton(false) + } + attendanceToExcuseList.clear() + return true + } + + fun onDestroyActionMode() { + view?.apply { + showExcuseCheckboxes(false) + showExcuseButton(true) } } @@ -157,6 +213,7 @@ class AttendancePresenter @Inject constructor( showEmpty(it.isEmpty()) showErrorView(false) showContent(it.isNotEmpty()) + showExcuseButton(it.any { item -> item.attendance.excusable }) } analytics.logEvent("load_attendance", "items" to it.size, "force_refresh" to forceRefresh) }) { @@ -167,6 +224,39 @@ class AttendancePresenter @Inject constructor( } } + private fun excuseAbsence(reason: String?, toExcuseList: List) { + Timber.i("Excusing absence started") + disposable.apply { + add(studentRepository.getCurrentStudent() + .delay(200, MILLISECONDS) + .flatMap { semesterRepository.getCurrentSemester(it) } + .flatMap { attendanceRepository.excuseForAbsence(it, toExcuseList, reason) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doOnSubscribe { + view?.apply { + showProgress(true) + showContent(false) + showExcuseButton(false) + } + } + .subscribe({ + Timber.i("Excusing for absence result: Success") + analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size) + attendanceToExcuseList.clear() + view?.apply { + showExcuseButton(false) + showMessage(excuseSuccessString) + } + loadData(currentDate, true) + }) { + Timber.i("Excusing for absence result: An exception occurred") + view?.showProgress(false) + errorHandler.dispatch(it) + }) + } + } + private fun showErrorViewOnError(message: String, error: Throwable) { view?.run { if (isViewEmpty) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt index a6d0d4ba9..03e95053f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt @@ -10,6 +10,12 @@ interface AttendanceView : BaseView { val currentStackSize: Int? + val excuseSuccessString: String + + val excuseNoSelectionString: String + + val excuseActionMode: Boolean + fun initView() fun updateData(data: List) @@ -38,11 +44,21 @@ interface AttendanceView : BaseView { fun showNextButton(show: Boolean) + fun showExcuseButton(show: Boolean) + fun showAttendanceDialog(lesson: Attendance) fun showDatePickerDialog(currentDate: LocalDate) + fun showExcuseDialog() + fun openSummaryView() + fun startActionMode() + + fun showExcuseCheckboxes(show: Boolean) + + fun finishActionMode() + fun popView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index 991c1d106..ae4be9530 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -36,6 +36,7 @@ import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.safelyPopFragments import io.github.wulkanowy.utils.setOnViewChangeListener import kotlinx.android.synthetic.main.activity_main.* +import timber.log.Timber import javax.inject.Inject class MainActivity : BaseActivity(), MainView { @@ -167,6 +168,11 @@ class MainActivity : BaseActivity(), MainView { (navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentReselected() } + override fun notifyMenuViewChanged() { + Timber.d("Menu view changed") + (navController.currentStack?.getOrNull(0) as? MainView.MainChildView)?.onFragmentChanged() + } + fun showDialogFragment(dialog: DialogFragment) { navController.showDialogFragment(dialog) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt index 41f4d5f2c..50e5896fe 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainModule.kt @@ -13,6 +13,7 @@ import io.github.wulkanowy.ui.modules.about.license.LicenseFragment import io.github.wulkanowy.ui.modules.about.license.LicenseModule import io.github.wulkanowy.ui.modules.account.AccountDialog import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment +import io.github.wulkanowy.ui.modules.attendance.AttendanceModule import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment import io.github.wulkanowy.ui.modules.exam.ExamFragment import io.github.wulkanowy.ui.modules.grade.GradeFragment @@ -51,7 +52,7 @@ abstract class MainModule { } @PerFragment - @ContributesAndroidInjector + @ContributesAndroidInjector(modules = [AttendanceModule::class]) abstract fun bindAttendanceFragment(): AttendanceFragment @PerFragment diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt index 1c0fd39f3..8eccbc67e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -75,6 +75,7 @@ class MainPresenter @Inject constructor( notifyMenuViewReselected() false } else { + notifyMenuViewChanged() switchMenuView(index) true } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt index 033b6a1f6..0d8b14169 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainView.kt @@ -26,6 +26,8 @@ interface MainView : BaseView { fun notifyMenuViewReselected() + fun notifyMenuViewChanged() + fun setViewTitle(title: String) fun popView(depth: Int = 1) @@ -33,6 +35,8 @@ interface MainView : BaseView { interface MainChildView { fun onFragmentReselected() + + fun onFragmentChanged() {} } interface TitledView { diff --git a/app/src/main/res/drawable-night/background_header_note.xml b/app/src/main/res/drawable-night/background_header_note.xml index 2983b32c6..6b594e7c6 100644 --- a/app/src/main/res/drawable-night/background_header_note.xml +++ b/app/src/main/res/drawable-night/background_header_note.xml @@ -1,5 +1,5 @@ - - + + diff --git a/app/src/main/res/drawable/background_header_note.xml b/app/src/main/res/drawable/background_header_note.xml index 8e92e3abf..c21e55c6b 100644 --- a/app/src/main/res/drawable/background_header_note.xml +++ b/app/src/main/res/drawable/background_header_note.xml @@ -1,5 +1,5 @@ - - - \ No newline at end of file + + + diff --git a/app/src/main/res/drawable/ic_all_done.xml b/app/src/main/res/drawable/ic_all_done.xml new file mode 100644 index 000000000..bb657f6ec --- /dev/null +++ b/app/src/main/res/drawable/ic_all_done.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_all_done_all.xml b/app/src/main/res/drawable/ic_all_done_all.xml new file mode 100644 index 000000000..e27672efa --- /dev/null +++ b/app/src/main/res/drawable/ic_all_done_all.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_excuse_denied.xml b/app/src/main/res/drawable/ic_excuse_denied.xml new file mode 100644 index 000000000..218cfbdc4 --- /dev/null +++ b/app/src/main/res/drawable/ic_excuse_denied.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_excuse_waiting.xml b/app/src/main/res/drawable/ic_excuse_waiting.xml new file mode 100644 index 000000000..863418b7b --- /dev/null +++ b/app/src/main/res/drawable/ic_excuse_waiting.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/dialog_excuse.xml b/app/src/main/res/layout/dialog_excuse.xml new file mode 100644 index 000000000..4ed9294ce --- /dev/null +++ b/app/src/main/res/layout/dialog_excuse.xml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_grade.xml b/app/src/main/res/layout/dialog_grade.xml index fad7bae57..be0570c20 100644 --- a/app/src/main/res/layout/dialog_grade.xml +++ b/app/src/main/res/layout/dialog_grade.xml @@ -40,16 +40,16 @@ @@ -57,16 +57,17 @@ android:id="@+id/gradeDialogHeader" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:minHeight="120dp" - android:gravity="center_vertical" - android:layout_toLeftOf="@+id/gradeDialogValueLayout" + android:layout_marginEnd="12dp" + android:layout_marginRight="12dp" android:layout_toStartOf="@+id/gradeDialogValueLayout" + android:layout_toLeftOf="@+id/gradeDialogValueLayout" android:layout_alignParentStart="true" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" - android:layout_marginRight="12dp" - android:layout_marginEnd="12dp" + android:gravity="center_vertical" + android:minHeight="120dp" android:orientation="vertical"> + + + diff --git a/app/src/main/res/layout/item_attendance.xml b/app/src/main/res/layout/item_attendance.xml index 95a8f0174..56bcab440 100644 --- a/app/src/main/res/layout/item_attendance.xml +++ b/app/src/main/res/layout/item_attendance.xml @@ -13,16 +13,40 @@ android:paddingBottom="7dp" tools:context=".ui.modules.attendance.AttendanceItem"> - + android:orientation="vertical"> + + + + + + + + + + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 458f64b45..f5f9f45d1 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -141,6 +141,11 @@ %1$d nieobecności %1$d nieobecności + Powód nieobecności (opcjonalny) + Wyślij + Usprawiedliwiono pomyślnie! + Musisz wybrać co najmniej jedną nieobecność! + Usprawiedliw diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8bdc54105..08b17825a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -135,6 +135,11 @@ %1$d absence %1$d absences + Absence reason (optional) + Send + Absence excused successfully! + You must select at least one absence! + Excuse diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 68c0800b1..8f30e3457 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -16,6 +16,7 @@ + + +

%SUBJECT%

+
+
+ %INFO% +
+ +
+
+

Treść wiadomości

+ %CONTENT% +
+ + diff --git a/app/src/main/assets/wulkanowy-logo-black.svg b/app/src/main/assets/wulkanowy-logo-black.svg new file mode 100644 index 000000000..9bfbe2c02 --- /dev/null +++ b/app/src/main/assets/wulkanowy-logo-black.svg @@ -0,0 +1,74 @@ + +image/svg+xml \ No newline at end of file diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt index 436dee53f..a94d2cfc8 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt @@ -63,7 +63,7 @@ class MessagePreviewAdapter @Inject constructor() : @SuppressLint("SetTextI18n") private fun bindMessage(holder: MessageViewHolder, message: Message) { with(holder.binding) { - messagePreviewSubject.text = if (message.subject.isNotBlank()) message.subject else root.context.getString(R.string.message_no_subject) + messagePreviewSubject.text = message.subject.ifBlank { root.context.getString(R.string.message_no_subject) } messagePreviewDate.text = root.context.getString(R.string.message_date, message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")) messagePreviewContent.text = message.content messagePreviewAuthor.text = if (message.folderId == MessageFolder.SENT.id) "${root.context.getString(R.string.message_to)} ${message.recipient}" diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 99eede15a..575db75b9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -1,12 +1,20 @@ package io.github.wulkanowy.ui.modules.message.preview +import android.os.Build import android.os.Bundle +import android.print.PrintAttributes +import android.print.PrintManager import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.View.GONE import android.view.View.VISIBLE +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.annotation.RequiresApi +import androidx.core.content.getSystemService import androidx.recyclerview.widget.LinearLayoutManager import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message @@ -17,6 +25,8 @@ import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.message.MessageFragment import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity +import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.shareText import javax.inject.Inject class MessagePreviewFragment : @@ -29,18 +39,31 @@ class MessagePreviewFragment : @Inject lateinit var previewAdapter: MessagePreviewAdapter + @Inject + lateinit var appInfo: AppInfo + private var menuReplyButton: MenuItem? = null private var menuForwardButton: MenuItem? = null private var menuDeleteButton: MenuItem? = null + private var menuShareButton: MenuItem? = null + + private var menuPrintButton: MenuItem? = null + override val titleStringId: Int get() = R.string.message_title override val deleteMessageSuccessString: String get() = getString(R.string.message_delete_success) + override val messageNoSubjectString: String + get() = getString(R.string.message_no_subject) + + override val printHTML: String + get() = requireContext().assets.open("message-print-page.html").bufferedReader().use { it.readText() } + companion object { const val MESSAGE_ID_KEY = "message_id" @@ -77,6 +100,8 @@ class MessagePreviewFragment : menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply) menuForwardButton = menu.findItem(R.id.messagePreviewMenuForward) menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete) + menuShareButton = menu.findItem(R.id.messagePreviewMenuShare) + menuPrintButton = menu.findItem(R.id.messagePreviewMenuPrint) presenter.onCreateOptionsMenu() } @@ -85,6 +110,8 @@ class MessagePreviewFragment : R.id.messagePreviewMenuReply -> presenter.onReply() R.id.messagePreviewMenuForward -> presenter.onForward() R.id.messagePreviewMenuDelete -> presenter.onMessageDelete() + R.id.messagePreviewMenuShare -> presenter.onShare() + R.id.messagePreviewMenuPrint -> presenter.onPrint() else -> false } } @@ -108,6 +135,8 @@ class MessagePreviewFragment : menuReplyButton?.isVisible = show menuForwardButton?.isVisible = show menuDeleteButton?.isVisible = show + menuShareButton?.isVisible = show + menuPrintButton?.isVisible = show && appInfo.systemVersion >= Build.VERSION_CODES.LOLLIPOP } override fun setDeletedOptionsLabels() { @@ -138,6 +167,38 @@ class MessagePreviewFragment : context?.let { it.startActivity(SendMessageActivity.getStartIntent(it, message)) } } + override fun shareText(text: String, subject: String) { + context?.shareText(text, subject) + } + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + override fun printDocument(html: String, jobName: String) { + val webView = WebView(activity) + webView.webViewClient = object : WebViewClient() { + + override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false + + override fun onPageFinished(view: WebView, url: String) { + createWebPrintJob(view, jobName) + } + } + + webView.loadDataWithBaseURL("file:///android_asset/", html, "text/HTML", "UTF-8", null) + } + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + private fun createWebPrintJob(webView: WebView, jobName: String) { + activity?.getSystemService()?.let { printManager -> + val printAdapter = webView.createPrintDocumentAdapter(jobName) + + printManager.print( + jobName, + printAdapter, + PrintAttributes.Builder().build() + ) + } + } + override fun popView() { (activity as MainActivity).popView() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index 24678c70e..db7996bca 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -1,12 +1,17 @@ package io.github.wulkanowy.ui.modules.message.preview +import android.annotation.SuppressLint +import android.os.Build import io.github.wulkanowy.data.db.entities.Message +import io.github.wulkanowy.data.db.entities.MessageAttachment import io.github.wulkanowy.data.repositories.message.MessageRepository import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider +import io.github.wulkanowy.utils.toFormattedString import timber.log.Timber import javax.inject.Inject @@ -15,11 +20,14 @@ class MessagePreviewPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val messageRepository: MessageRepository, - private val analytics: FirebaseAnalyticsHelper + private val analytics: FirebaseAnalyticsHelper, + private var appInfo: AppInfo ) : BasePresenter(errorHandler, studentRepository, schedulers) { var message: Message? = null + var attachments: List? = null + private lateinit var lastError: Throwable private var retryCallback: () -> Unit = {} @@ -56,6 +64,7 @@ class MessagePreviewPresenter @Inject constructor( .subscribe({ message -> Timber.i("Loading message ${message.message.messageId} preview result: Success ") this@MessagePreviewPresenter.message = message.message + this@MessagePreviewPresenter.attachments = message.attachments view?.apply { setMessageWithAttachment(message) initOptions() @@ -87,6 +96,60 @@ class MessagePreviewPresenter @Inject constructor( } else false } + fun onShare(): Boolean { + message?.let { + var text = "Temat: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}\n" + when (it.sender.isNotEmpty()) { + true -> "Od: ${it.sender}\n" + false -> "Do: ${it.recipient}\n" + } + "Data: ${it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${it.content}" + + attachments?.let { attachments -> + if (attachments.isNotEmpty()) { + text += "\n\nZałączniki:" + + attachments.forEach { attachment -> + text += "\n${attachment.filename}: ${attachment.url}" + } + } + } + + view?.shareText(text, "FW: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}") + return true + } + return false + } + + @SuppressLint("NewApi") + fun onPrint(): Boolean { + if (appInfo.systemVersion < Build.VERSION_CODES.LOLLIPOP) return false + message?.let { + val dateString = it.date.toFormattedString("yyyy-MM-dd HH:mm:ss") + val infoContent = "

Data wysłania

$dateString
" + when { + it.sender.isNotEmpty() -> "

Od

${it.sender}
" + else -> "

Do

${it.recipient}
" + } + + val messageContent = "

${it.content}

" + .replace(Regex("[\\n\\r]{2,}"), "

") + .replace(Regex("[\\n\\r]"), "
") + + val jobName = "Wiadomość " + when { + it.sender.isNotEmpty() -> "od ${it.sender}" + else -> "do ${it.recipient}" + } + " $dateString: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }} | Wulkanowy" + + view?.apply { + val html = printHTML + .replace("%SUBJECT%", it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }) + .replace("%CONTENT%", messageContent) + .replace("%INFO%", infoContent) + printDocument(html, jobName) + } + return true + } + return false + } + private fun deleteMessage() { message?.let { message -> disposable.add(studentRepository.getCurrentStudent() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt index 3d620459c..0fdb4bda3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt @@ -1,5 +1,7 @@ package io.github.wulkanowy.ui.modules.message.preview +import android.os.Build +import androidx.annotation.RequiresApi import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.ui.base.BaseView @@ -8,6 +10,10 @@ interface MessagePreviewView : BaseView { val deleteMessageSuccessString: String + val messageNoSubjectString: String + + val printHTML: String + fun initView() fun setMessageWithAttachment(item: MessageWithAttachment) @@ -34,5 +40,10 @@ interface MessagePreviewView : BaseView { fun openMessageForward(message: Message?) + fun shareText(text: String, subject: String) + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + fun printDocument(html: String, jobName: String) + fun popView() } diff --git a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt index 2b40cb476..cf715e657 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt @@ -71,4 +71,17 @@ fun Context.openDialer(phone: String) { startActivity(intent) } +fun Context.shareText(text: String, subject: String?) { + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_TEXT, text) + if (subject != null) { + putExtra(Intent.EXTRA_SUBJECT, subject) + } + type = "text/plain" + } + val shareIntent = Intent.createChooser(sendIntent, null) + startActivity(shareIntent) +} + fun Context.dpToPx(dp: Float) = dp * resources.displayMetrics.densityDpi / DENSITY_DEFAULT diff --git a/app/src/main/res/drawable/ic_menu_message_print.xml b/app/src/main/res/drawable/ic_menu_message_print.xml new file mode 100644 index 000000000..204b0f6e3 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_message_print.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/drawable/ic_menu_message_share.xml b/app/src/main/res/drawable/ic_menu_message_share.xml new file mode 100644 index 000000000..67a8ee494 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_message_share.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/menu/action_menu_message_preview.xml b/app/src/main/res/menu/action_menu_message_preview.xml index dfc12e234..4c1332e10 100644 --- a/app/src/main/res/menu/action_menu_message_preview.xml +++ b/app/src/main/res/menu/action_menu_message_preview.xml @@ -22,4 +22,18 @@ android:title="@string/message_delete" app:iconTint="@color/material_on_surface_emphasis_medium" app:showAsAction="ifRoom" /> + + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 2bba1640b..a8eccabf0 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -204,6 +204,8 @@ Przenieś do kosza Usuń trwale Wiadomość usunięta pomyślnie + Udostępnij + Drukuj Temat Treść Wiadomość wysłana pomyślnie diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3efb53cb2..1eaebf284 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -210,6 +210,8 @@ Move to trash Delete permanently Message deleted successfully + Share + Print Subject Content Message sent successfully From 6e1ddb482e90008dcf8af110362abedc4d10d42b Mon Sep 17 00:00:00 2001 From: Dominik Korsa Date: Sun, 14 Jun 2020 14:05:24 +0200 Subject: [PATCH 234/236] Message fuzzy search (#869) --- app/build.gradle | 3 +- .../modules/message/tab/MessageTabAdapter.kt | 56 ++++--- .../modules/message/tab/MessageTabFragment.kt | 2 +- .../message/tab/MessageTabPresenter.kt | 147 ++++++++++++------ 4 files changed, 130 insertions(+), 78 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 44896664a..82d92f25a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -142,7 +142,7 @@ dependencies { implementation "androidx.constraintlayout:constraintlayout:1.1.3" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" implementation "com.google.android.material:material:1.1.0" - implementation "com.github.wulkanowy:material-chips-input:2.0.1" + implementation "com.github.wulkanowy:material-chips-input:2.1.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "me.zhanghai.android.materialprogressbar:library:1.6.1" @@ -180,6 +180,7 @@ dependencies { implementation 'com.wdullaer:materialdatetimepicker:4.2.3' implementation "io.coil-kt:coil:0.11.0" implementation "io.github.wulkanowy:AppKillerManager:3.0.0" + implementation 'me.xdrop:fuzzywuzzy:1.3.1' playImplementation 'com.google.firebase:firebase-analytics:17.4.3' playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.0.7' diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt index ece6773fd..b58508a98 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt @@ -4,10 +4,9 @@ import android.graphics.Typeface import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.NO_POSITION -import androidx.recyclerview.widget.SortedList -import androidx.recyclerview.widget.SortedListAdapterCallback import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.repositories.message.MessageFolder @@ -20,39 +19,23 @@ class MessageTabAdapter @Inject constructor() : var onClickListener: (Message, position: Int) -> Unit = { _, _ -> } - private val items = SortedList(Message::class.java, object : - SortedListAdapterCallback(this) { + private var items = mutableListOf() - override fun compare(item1: Message, item2: Message): Int { - return item2.date.compareTo(item1.date) - } - - override fun areContentsTheSame(oldItem: Message?, newItem: Message?): Boolean { - return oldItem == newItem - } - - override fun areItemsTheSame(item1: Message, item2: Message): Boolean { - return item1 == item2 - } - }) - - fun replaceAll(models: List) { - items.beginBatchedUpdates() - for (i in items.size() - 1 downTo 0) { - val model = items.get(i) - if (model !in models) { - items.remove(model) - } - } - items.addAll(models) - items.endBatchedUpdates() + fun setDataItems(data: List) { + val diffResult = DiffUtil.calculateDiff(MessageTabDiffUtil(items, data)) + items = data.toMutableList() + diffResult.dispatchUpdatesTo(this) } fun updateItem(position: Int, item: Message) { - items.updateItemAt(position, item) + val currentItem = items[position] + items[position] = item + if (item != currentItem) { + notifyItemChanged(position) + } } - override fun getItemCount() = items.size() + override fun getItemCount() = items.size override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( ItemMessageBinding.inflate(LayoutInflater.from(parent.context), parent, false) @@ -85,4 +68,19 @@ class MessageTabAdapter @Inject constructor() : } class ItemViewHolder(val binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root) + + private class MessageTabDiffUtil(private val old: List, private val new: List) : + DiffUtil.Callback() { + override fun getOldListSize(): Int = old.size + + override fun getNewListSize(): Int = new.size + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return old[oldItemPosition].id == new[newItemPosition].id + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return old[oldItemPosition] == new[newItemPosition] + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt index 909bb6873..9954c6428 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt @@ -90,7 +90,7 @@ class MessageTabFragment : BaseFragment(R.layout.frag } override fun updateData(data: List) { - tabAdapter.replaceAll(data) + tabAdapter.setDataItems(data) } override fun updateItem(item: Message, position: Int) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index 221762d16..533f5ac85 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.message.tab -import android.annotation.SuppressLint import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.repositories.message.MessageFolder import io.github.wulkanowy.data.repositories.message.MessageRepository @@ -11,8 +10,13 @@ import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.toFormattedString +import io.reactivex.subjects.PublishSubject +import me.xdrop.fuzzywuzzy.FuzzySearch import timber.log.Timber +import java.util.Locale +import java.util.concurrent.TimeUnit import javax.inject.Inject +import kotlin.math.pow class MessageTabPresenter @Inject constructor( schedulers: SchedulersProvider, @@ -31,9 +35,12 @@ class MessageTabPresenter @Inject constructor( private var messages = emptyList() + private val searchQuery = PublishSubject.create() + fun onAttachView(view: MessageTabView, folder: MessageFolder) { super.onAttachView(view) view.initView() + initializeSearchStream() errorHandler.showErrorMessage = ::showErrorViewOnError this.folder = folder } @@ -76,38 +83,35 @@ class MessageTabPresenter @Inject constructor( private fun loadData(forceRefresh: Boolean) { Timber.i("Loading $folder message data started") - disposable.apply { - clear() - add(studentRepository.getCurrentStudent() - .flatMap { student -> - semesterRepository.getCurrentSemester(student) - .flatMap { messageRepository.getMessages(student, it, folder, forceRefresh) } + disposable.add(studentRepository.getCurrentStudent() + .flatMap { student -> + semesterRepository.getCurrentSemester(student) + .flatMap { messageRepository.getMessages(student, it, folder, forceRefresh) } + } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .doFinally { + view?.run { + showRefresh(false) + showProgress(false) + enableSwipe(true) + notifyParentDataLoaded() } - .subscribeOn(schedulers.backgroundThread) - .observeOn(schedulers.mainThread) - .doFinally { - view?.run { - showRefresh(false) - showProgress(false) - enableSwipe(true) - notifyParentDataLoaded() - } - } - .subscribe({ - Timber.i("Loading $folder message result: Success") - messages = it - onSearchQueryTextChange(lastSearchQuery) - analytics.logEvent( - "load_data", - "type" to "messages", - "items" to it.size, - "folder" to folder.name - ) - }) { - Timber.i("Loading $folder message result: An exception occurred") - errorHandler.dispatch(it) - }) - } + } + .subscribe({ + Timber.i("Loading $folder message result: Success") + messages = it + view?.updateData(getFilteredData(lastSearchQuery)) + analytics.logEvent( + "load_data", + "type" to "messages", + "items" to it.size, + "folder" to folder.name + ) + }) { + Timber.i("Loading $folder message result: An exception occurred") + errorHandler.dispatch(it) + }) } private fun showErrorViewOnError(message: String, error: Throwable) { @@ -121,25 +125,36 @@ class MessageTabPresenter @Inject constructor( } } - @SuppressLint("DefaultLocale") fun onSearchQueryTextChange(query: String) { - lastSearchQuery = query + if (query != searchQuery.toString()) + searchQuery.onNext(query) + } - val lowerCaseQuery = query.toLowerCase() - val filteredList = mutableListOf() - messages.forEach { - if (lowerCaseQuery in it.subject.toLowerCase() || - lowerCaseQuery in it.sender.toLowerCase() || - lowerCaseQuery in it.recipient.toLowerCase() || - lowerCaseQuery in it.date.toFormattedString() - ) { - filteredList.add(it) + private fun initializeSearchStream() { + disposable.add(searchQuery + .debounce(250, TimeUnit.MILLISECONDS) + .map { query -> + lastSearchQuery = query + getFilteredData(query) } + .subscribeOn(schedulers.backgroundThread) + .observeOn(schedulers.mainThread) + .subscribe({ + Timber.d("Applying filter. Full list: ${messages.size}, filtered: ${it.size}") + updateData(it) + }) { Timber.e(it) }) + } + + private fun getFilteredData(query: String): List { + return if (query.trim().isEmpty()) { + messages.sortedByDescending { it.date } + } else { + messages + .map { it to calculateMatchRatio(it, query) } + .sortedByDescending { it.second } + .filter { it.second > 5000 } + .map { it.first } } - - Timber.d("Applying filter. Full list: ${messages.size}, filtered: ${filteredList.size}") - - updateData(filteredList) } private fun updateData(data: List) { @@ -151,4 +166,42 @@ class MessageTabPresenter @Inject constructor( resetListPosition() } } + + private fun calculateMatchRatio(message: Message, query: String): Int { + val subjectRatio = FuzzySearch.tokenSortPartialRatio( + query.toLowerCase(Locale.getDefault()), + message.subject + ) + + val senderOrRecipientRatio = FuzzySearch.tokenSortPartialRatio( + query.toLowerCase(Locale.getDefault()), + if (message.sender.isNotEmpty()) message.sender.toLowerCase(Locale.getDefault()) + else message.recipient.toLowerCase(Locale.getDefault()) + ) + + val dateRatio = listOf( + FuzzySearch.ratio( + query.toLowerCase(Locale.getDefault()), + message.date.toFormattedString("dd.MM").toLowerCase(Locale.getDefault()) + ), + FuzzySearch.ratio( + query.toLowerCase(Locale.getDefault()), + message.date.toFormattedString("dd.MM.yyyy").toLowerCase(Locale.getDefault()) + ), + FuzzySearch.ratio( + query.toLowerCase(Locale.getDefault()), + message.date.toFormattedString("d MMMM").toLowerCase(Locale.getDefault()) + ), + FuzzySearch.ratio( + query.toLowerCase(Locale.getDefault()), + message.date.toFormattedString("d MMMM yyyy").toLowerCase(Locale.getDefault()) + ) + ).max() ?: 0 + + + return (subjectRatio.toDouble().pow(2) + + senderOrRecipientRatio.toDouble().pow(2) + + dateRatio.toDouble().pow(2) * 2 + ).toInt() + } } From dfe7981e7fa71321267f2d1daf545a7ae65e9eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Borcz?= Date: Sun, 14 Jun 2020 22:37:58 +0200 Subject: [PATCH 235/236] New Crowdin translations (#874) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mikołaj Pich --- .../main/res/values-de/preferences_values.xml | 4 +-- app/src/main/res/values-de/strings.xml | 30 ++++++++++++++--- app/src/main/res/values-pl/strings.xml | 2 +- .../main/res/values-ru/preferences_values.xml | 2 +- app/src/main/res/values-ru/strings.xml | 32 ++++++++++++++++++- .../main/res/values-uk/preferences_values.xml | 2 +- app/src/main/res/values-uk/strings.xml | 32 ++++++++++++++++++- 7 files changed, 93 insertions(+), 11 deletions(-) diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml index 496287984..11935b49b 100644 --- a/app/src/main/res/values-de/preferences_values.xml +++ b/app/src/main/res/values-de/preferences_values.xml @@ -36,8 +36,8 @@ Durchschnittsnote für das 2. Semester - Average of grades from both semesters - Durchschnitt der Bewertungen für das ganze Jahr + Durchschnitt der Noten aus beiden Semestern + Durchschnitt der Noten aus dem ganzen Jahr Nicht zeigen diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 4b1669371..7cc44fd97 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -103,10 +103,26 @@ Neue Note Neue Noten + + New predicted grade + New predicted grades + + + New final grade + New final grades + Du hast %1$d Note bekommen Du hast %1$d Noten bekommen + + You received %1$d predicted grade + You received %1$d predicted grades + + + You received %1$d final grade + You received %1$d final grades + Lektion Klassenzimmer @@ -172,6 +188,8 @@ In den Korb wandern Dauerhaft löschen Nachricht erfolgreich gelöscht + Share + Print Thema Inhalt Nachricht erfolgreich gesendet @@ -214,7 +232,7 @@ Die heutige Glücksnummer ist Keine Information über die Glücksnummer. Glücksnummer für heute - Die heutige Glücksnummer ist: + Die heutige Glücksnummer ist: %d Mobile Geräte Keine Geräte @@ -245,6 +263,10 @@ Abmelden Wollen Sie sich von einem aktiven Studenten abmelden? Abmeldung von Student + Student account + Parent account + Mobile API mode + Hybrid mode Version der App Mitarbeiter @@ -270,8 +292,8 @@ Logs teilen Aktualisieren - Check for updates - Before reporting a bug, check first if an update with the bug fix is available + Auf Updates prüfen + Bevor Sie einen Fehler melden, prüfen Sie zuerst, ob ein Update mit der Fehlerbehebung verfügbar ist Inhalt Wiederhol @@ -289,7 +311,7 @@ Zurück Nächste Suchen - Suchen... + Suchen… Keine Lektionen Thema wählen diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index a8eccabf0..bd4545e9b 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -339,7 +339,7 @@ Poprzedni Następny Szukaj - Szukaj... + Szukaj… Brak lekcji Wybierz motyw diff --git a/app/src/main/res/values-ru/preferences_values.xml b/app/src/main/res/values-ru/preferences_values.xml index bd8bb844f..a41abf350 100644 --- a/app/src/main/res/values-ru/preferences_values.xml +++ b/app/src/main/res/values-ru/preferences_values.xml @@ -37,7 +37,7 @@ Средняя оценка со 2 семестра Average of grades from both semesters - Средняя оценка с целого года + Average of grades from the whole year Не показывать diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6eb8f7741..15c583173 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -107,12 +107,36 @@ Новые оценки Новые оценки + + New predicted grade + New predicted grades + New predicted grades + New predicted grades + + + New final grade + New final grades + New final grades + New final grades + Вы получили %1$d оценку Вы получили %1$d оценки Вы получили %1$d оценок Вы получили %1$d оценок + + You received %1$d predicted grade + You received %1$d predicted grades + You received %1$d predicted grades + You received %1$d predicted grades + + + You received %1$d final grade + You received %1$d final grades + You received %1$d final grades + You received %1$d final grades + Урок Аудитория @@ -180,6 +204,8 @@ Перенести в корзину Удалить навсегда Сообщение успешно удалено + Share + Print Тема Текст Сообщение успешно отправлено @@ -265,6 +291,10 @@ Выйти Вы точно хотите выйти из данного аккаунта? Выйти + Student account + Parent account + Mobile API mode + Hybrid mode Версия приложения Разработчики @@ -309,7 +339,7 @@ Предыдущий Следующий Поиск - Поиск... + Поиск… Нет уроков Выбрать тему diff --git a/app/src/main/res/values-uk/preferences_values.xml b/app/src/main/res/values-uk/preferences_values.xml index be2179c3e..9942621a5 100644 --- a/app/src/main/res/values-uk/preferences_values.xml +++ b/app/src/main/res/values-uk/preferences_values.xml @@ -37,7 +37,7 @@ Середня оцінка з 2 семестру Average of grades from both semesters - Середня оцінка за весь рік + Average of grades from the whole year Не показувати diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index ec4dbc155..423c4e12f 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -107,12 +107,36 @@ Нові оцінки Нові оцінки + + New predicted grade + New predicted grades + New predicted grades + New predicted grades + + + New final grade + New final grades + New final grades + New final grades + Ви отримали %1$d оцінку Ви отримали %1$d оцінки Ви отримали %1$d оцінок Ви отримали %1$d оцінок + + You received %1$d predicted grade + You received %1$d predicted grades + You received %1$d predicted grades + You received %1$d predicted grades + + + You received %1$d final grade + You received %1$d final grades + You received %1$d final grades + You received %1$d final grades + Урок Аудиторія @@ -180,6 +204,8 @@ Перемістити у кошик Видалити назавжди Повідомлення було успішно видалено + Share + Print Тема Зміст Повідомлення було успішно відправлено @@ -265,6 +291,10 @@ Вийти Ви впевнені, що хочете вийти з цього аккаунту? Вийти з аккаунту учня + Student account + Parent account + Mobile API mode + Hybrid mode Версія додатка Розробники @@ -309,7 +339,7 @@ Попередній Наступний Пошук - Пошук... + Пошук… Брак уроків Увібрати тему From c13f12f729114c5bfd4539f168fcd1dfd383b52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Pich?= Date: Sun, 14 Jun 2020 22:40:36 +0200 Subject: [PATCH 236/236] Version 0.19.0 --- .travis.yml | 2 +- app/build.gradle | 6 +++--- app/src/main/play/release-notes/pl-PL/default.txt | 10 ++++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 788aeffa7..a24058083 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ cache: branches: only: - develop - - 0.18.3 + - 0.19.0 android: licenses: diff --git a/app/build.gradle b/app/build.gradle index 82d92f25a..621e5a4fe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 17 targetSdkVersion 29 - versionCode 62 - versionName "0.18.3" + versionCode 63 + versionName "0.19.0" multiDexEnabled true resValue "string", "app_name", "Wulkanowy" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -124,7 +124,7 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:7dc0761" + implementation "io.github.wulkanowy:sdk:0.19.0" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "androidx.core:core-ktx:1.2.0" diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index db9e9a9a3..68045da4a 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,6 +1,8 @@ -Wersja 0.18.3 -- poprawiliśmy liczenie średniej i dodaliśmy nowy sposób jej liczenia w ustawieniach -- naprawiliśmy usuwanie wiadomości -- naprawiliśmy wysyłanie wiadomości na Lubelskim Portalu Oświatowym +Wersja 0.19.0 +- naprawiliśmy pokazywanie brakujących przedmiotów na liście podsumowania ocen +- ulepszyliśmy wygląd menadżera kont +- ulepszyliśmy wyszukiwarkę wiadomości +- dodaliśmy powiadomienia o proponowanych i końcowych ocenach +- dodaliśmy opcję udostępniania i drukowania wiadomości Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases