From ad9a2711c46fbb86eca6cbe5f120441c42d13891 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sun, 19 Dec 2021 22:46:41 +0100
Subject: [PATCH 001/669] New Crowdin updates (#1687)
---
app/src/main/res/values-cs/strings.xml | 12 ++++++------
app/src/main/res/values-de/strings.xml | 4 ++--
app/src/main/res/values-pl/strings.xml | 8 ++++----
app/src/main/res/values-ru/strings.xml | 16 ++++++++--------
app/src/main/res/values-sk/strings.xml | 10 +++++-----
app/src/main/res/values-uk/strings.xml | 16 ++++++++--------
6 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 93ea10a11..8e49d4f1c 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -253,10 +253,10 @@
- Nové zkoušky
- - Máte %d novou zkoušku
- - Máte %d nové zkoušky
- - Máte %d nových zkoušek
- - Máte %d nových zkoušek
+ - %d nová zkouška
+ - %d nové zkoušky
+ - %d nových zkoušek
+ - %d nových zkoušek
- %d zkouška
@@ -750,8 +750,8 @@
Nelze se připojit ke deníku. Servery mohou být přetíženy. Prosím zkuste to znovu později
Načítání dat se nezdařilo. Prosím zkuste to znovu později
Je vyžadována změna hesla pro deník
- Probíhá údržba UONET+ deník. Zkuste to později znovu
- Neznámá chyba denika UONET+. Prosím zkuste to znovu později
+ Probíhá údržba deníku UONET+. Zkuste to později znovu
+ Neznámá chyba deniku UONET+. Prosím zkuste to znovu později
Neznámá chyba aplikace. Prosím zkuste to znovu později
Vyskytla se neočekávaná chyba
Funkce je deaktivována přes vaší školou
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index f81724a21..83bbe307f 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -223,8 +223,8 @@
- Neue prüfungen
- - Du hast %d neue Prüfung
- - Du hast %d neue Prüfungen
+ - %d new exam
+ - %d new exams
- %d prüfung
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 93bdd5354..17fcb0884 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -253,10 +253,10 @@
- Nowe sprawdziany
- - Masz %d nowy sprawdzian
- - Masz %d nowe sprawdziany
- - Masz %d nowych sprawdzianów
- - Masz %d nowych sprawdzianów
+ - %d nowy sprawdzian
+ - %d nowe sprawdziany
+ - %d nowych sprawdzianów
+ - %d nowych sprawdzianów
- %d sprawdzian
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 33ec21826..e09e6d7da 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -253,10 +253,10 @@
- Новые экзамены
- - Вы получили %d новый экзамен
- - Вы получили %d новый экзамен
- - Вы получили %d новый экзамен
- - Вы получили %d новых экзаменов
+ - %d new exam
+ - %d new exams
+ - %d new exams
+ - %d new exams
- %d экзамен
@@ -671,9 +671,9 @@
Записывать официальные уведомления
Показывать push-уведомления
С помощью этой функции вы можете получить замену push-уведомлений, как в официальном приложении. Все, что вам нужно сделать, это разрешить Wulkanowy получать все уведомления в настройках системы.\n\nКак это работает?\nКогда вы получаете уведомление в Dziennik VULCAN, Wulkanowy будет уведомлен (это требует дополнительных прав) и запустит синхронизацию, чтобы отправить свое уведомление.\n\nТОЛЬКО ДЛЯ ПОЛЬЗОВАТЕЛЯ
- Upcoming lesson notifications
- You must allow the Wulkanowy app to set alarms and reminders in your system settings to use this feature.
- Go to settings
+ Показывать уведомления о будущих уроках
+ Вы должны разрешить приложению Wulkanowy установить будильник и напоминания в настройках системы, чтобы использовать эту функцию.
+ Перейти к настройкам
Синхронизация
Автоматическая синхронизация
Приостановить синхронизации во время каникул
@@ -695,7 +695,7 @@
Согласен
Политика конфиденциальности
Объявление загружается
- Thank you for your support, come back later for more ads
+ Спасибо за вашу поддержку, возвращайтесь позже для дополнительной рекламы
Расширенные
Внешний вид & Поведение
Уведомления
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 77e402add..584a923d5 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -253,10 +253,10 @@
- Nové skúšky
- - Máte %d novú skúšku
- - Máte %d nové skúšky
- - Máte %d nových skúšok
- - Máte %d nových skúšok
+ - %d nová skúška
+ - %d nové skúšky
+ - %d nových skúšok
+ - %d nových skúšok
- %d skúška
@@ -750,7 +750,7 @@
Nedá sa pripojiť ku denníku. Servery môžu byť preťažené. Prosím skúste to znova neskôr
Načítanie údajov zlyhalo. Skúste neskôr prosím
Je vyžadovaná zmena hesla pre denník
- Prebieha údržba UONET+ denník. Skúste to neskôr znova
+ Prebieha údržba denníka UONET+. Skúste to neskôr znova
Neznáma chyba dennika UONET+. Prosím skúste to znova neskôr
Neznáma chyba aplikácie. Prosím skúste to znova neskôr
Vyskytla sa neočakávaná chyba
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 8543a4708..5d24fedf2 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -253,10 +253,10 @@
- Нові іспити
- - Ви отримали %d новий іспит
- - Ви отримали %d новий іспит
- - Ви отримали %d новий іспит
- - Ви отримали %d нових іспитів
+ - %d new exam
+ - %d new exams
+ - %d new exams
+ - %d new exams
- %d екзамен
@@ -671,9 +671,9 @@
Захоплювати офіційні сповіщення програм
Показувати push-повідомлення
За допомогою цієї функції ви можете отримати заміну push -повідомлень, як у офіційному додатку. Все, що вам потрібно зробити, це дозволити Wulkanowy отримувати всі сповіщення у налаштуваннях вашої системи. \ N \ nЯк це працює? \ NКоли ви отримаєте сповіщення у Dziennik VULCAN, Wulkanowy отримає сповіщення (для цього призначені ці додаткові дозволи) і запустить синхронізація, яка може надсилати власне сповіщення. \ n \ n ТІЛЬКИ ДЛЯ РОЗШИРЕНИХ КОРИСТУВАЧІВ
- Upcoming lesson notifications
- You must allow the Wulkanowy app to set alarms and reminders in your system settings to use this feature.
- Go to settings
+ Показувати повідомлення о наступних уроках
+ Ви повинні дозволити Wulkanowy встановити будильник та нагадування у налаштуваннях вашої системи для використання цієї функції.
+ Перейти до налаштувань
Синхронізація
Автоматична синхронізація
Призупинено на час канікул
@@ -695,7 +695,7 @@
Погоджуюсь
Політика конфіденційності
Реклама завантажується
- Thank you for your support, come back later for more ads
+ Дякуємо за вашу підтримку, повертайтеся пізніше для більшої кількості оголошень
Додатково
Вигляд & Поведінка
Повідомлення
From cc22985dc5349d36ef40388c7062efea6865b69a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 20 Dec 2021 20:22:56 +0000
Subject: [PATCH 002/669] Bump firebase-bom from 29.0.2 to 29.0.3 (#1728)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index ea74cd562..23cd666ed 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -229,7 +229,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
- playImplementation platform('com.google.firebase:firebase-bom:29.0.2')
+ playImplementation platform('com.google.firebase:firebase-bom:29.0.3')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 094df212b411699bb786460c4447c4d3ddeeb362 Mon Sep 17 00:00:00 2001
From: Mateusz Idziejczak
Date: Tue, 21 Dec 2021 00:36:59 +0100
Subject: [PATCH 003/669] Add additional lessons feature (#1550)
---
app/build.gradle | 2 +-
.../45.json | 2430 +++++++++++++++++
app/src/main/AndroidManifest.xml | 1 -
.../github/wulkanowy/data/db/AppDatabase.kt | 8 +-
.../data/db/dao/TimetableAdditionalDao.kt | 11 +-
.../wulkanowy/data/db/entities/Semester.kt | 1 -
.../data/db/entities/TimetableAdditional.kt | 7 +
.../data/repositories/TimetableRepository.kt | 13 +-
.../modules/attendance/AttendanceFragment.kt | 4 +-
.../modules/homework/add/HomeworkAddDialog.kt | 17 +-
.../history/LuckyNumberHistoryFragment.kt | 4 +-
.../ui/modules/timetable/TimetableFragment.kt | 8 +-
.../additional/AdditionalLessonsAdapter.kt | 9 +-
.../additional/AdditionalLessonsFragment.kt | 50 +-
.../additional/AdditionalLessonsPresenter.kt | 38 +-
.../additional/AdditionalLessonsView.kt | 6 +
.../add/AdditionalLessonAddDialog.kt | 188 ++
.../add/AdditionalLessonAddPresenter.kt | 166 ++
.../additional/add/AdditionalLessonAddView.kt | 30 +
.../completed/CompletedLessonsFragment.kt | 21 +-
.../github/wulkanowy/utils/TimeExtension.kt | 55 +-
app/src/main/res/drawable/ic_all_clock.xml | 12 +
app/src/main/res/drawable/ic_calendat_all.xml | 10 +
.../main/res/layout/dialog_additional_add.xml | 156 ++
app/src/main/res/layout/dialog_grade.xml | 1 -
.../main/res/layout/dialog_homework_add.xml | 20 +-
.../layout/fragment_timetable_additional.xml | 14 +
.../res/layout/item_timetable_additional.xml | 18 +-
app/src/main/res/values/strings.xml | 11 +
build.gradle | 2 +-
30 files changed, 3222 insertions(+), 91 deletions(-)
create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json
create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddView.kt
create mode 100644 app/src/main/res/drawable/ic_all_clock.xml
create mode 100644 app/src/main/res/drawable/ic_calendat_all.xml
create mode 100644 app/src/main/res/layout/dialog_additional_add.xml
diff --git a/app/build.gradle b/app/build.gradle
index 23cd666ed..1ff8b9268 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -167,7 +167,7 @@ huaweiPublish {
ext {
work_manager = "2.7.1"
android_hilt = "1.0.0"
- room = "2.3.0"
+ room = "2.4.0"
chucker = "3.5.2"
mockk = "1.12.1"
coroutines = "1.5.2"
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json
new file mode 100644
index 000000000..57f3d431d
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json
@@ -0,0 +1,2430 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 45,
+ "identityHash": "f310243440ca00cbc35e62ebaca5c7d8",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "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"
+ ],
+ "orders": [],
+ "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}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "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"
+ ],
+ "orders": [],
+ "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}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "recipient",
+ "columnName": "recipient_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "removed",
+ "columnName": "removed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+ "fields": [
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "oneDriveId",
+ "columnName": "one_drive_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "real_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ReportingUnits",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shortName",
+ "columnName": "short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderName",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roles",
+ "columnName": "roles",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceId",
+ "columnName": "device_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDismissible",
+ "columnName": "is_dismissible",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "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, 'f310243440ca00cbc35e62ebaca5c7d8')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index de4a80b06..72fee08a7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -114,7 +114,6 @@
-
{
@Query("SELECT * FROM TimetableAdditional WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
- fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow>
+ fun loadAll(
+ diaryId: Int,
+ studentId: Int,
+ from: LocalDate,
+ end: LocalDate
+ ): Flow>
+
+ @Query("DELETE FROM TimetableAdditional WHERE repeat_id = :repeatId")
+ suspend fun deleteAllByRepeatId(repeatId: UUID)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt
index 3b1f0add5..3dd7ee0cb 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt
@@ -42,7 +42,6 @@ data class Semester(
@PrimaryKey(autoGenerate = true)
var id: Long = 0
-
@ColumnInfo(name = "is_current")
var current: Boolean = false
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt
index c1f1365f9..db32de874 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt
@@ -6,6 +6,7 @@ import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.LocalDate
import java.time.LocalDateTime
+import java.util.UUID
@Entity(tableName = "TimetableAdditional")
data class TimetableAdditional(
@@ -27,4 +28,10 @@ data class TimetableAdditional(
@PrimaryKey(autoGenerate = true)
var id: Long = 0
+
+ @ColumnInfo(name = "repeat_id", defaultValue = "NULL")
+ var repeatId: UUID? = null
+
+ @ColumnInfo(name = "is_added_by_user", defaultValue = "0")
+ var isAddedByUser: Boolean = false
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
index 8be621122..1f7bb1cff 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
@@ -152,7 +152,8 @@ class TimetableRepository @Inject constructor(
old: List,
new: List
) {
- timetableAdditionalDb.deleteAll(old uniqueSubtract new)
+ val oldFiltered = old.filter { !it.isAddedByUser }
+ timetableAdditionalDb.deleteAll(oldFiltered uniqueSubtract new)
timetableAdditionalDb.insertAll(new uniqueSubtract old)
}
@@ -160,4 +161,14 @@ class TimetableRepository @Inject constructor(
timetableHeaderDb.deleteAll(old uniqueSubtract new)
timetableHeaderDb.insertAll(new uniqueSubtract old)
}
+
+ suspend fun saveAdditionalList(additionalList: List) =
+ timetableAdditionalDb.insertAll(additionalList)
+
+ suspend fun deleteAdditional(additional: TimetableAdditional, deleteSeries: Boolean) =
+ if (deleteSeries) {
+ timetableAdditionalDb.deleteAllByRepeatId(additional.repeatId!!)
+ } else {
+ timetableAdditionalDb.deleteAll(listOf(additional))
+ }
}
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 bd9a66926..9b5a3fa36 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
@@ -29,8 +29,8 @@ import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
+import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.schoolYearStart
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
@@ -225,7 +225,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag
}
override fun showDatePickerDialog(currentDate: LocalDate) {
- val baseDate = currentDate.schoolYearStart
+ val baseDate = currentDate.firstSchoolDayInSchoolYear
val rangeStart = baseDate.toTimestamp()
val rangeEnd = LocalDate.now().plusWeeks(1).toTimestamp()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt
index 12168f142..0f285b13d 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt
@@ -11,6 +11,8 @@ import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.DialogHomeworkAddBinding
import io.github.wulkanowy.ui.base.BaseDialogFragment
+import io.github.wulkanowy.utils.SchoolDaysValidator
+import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.toTimestamp
@@ -98,14 +100,17 @@ class HomeworkAddDialog : BaseDialogFragment(), Homewo
}
override fun showDatePickerDialog(currentDate: LocalDate) {
+ val rangeStart = LocalDate.now().toTimestamp()
+ val rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear.toTimestamp()
val constraintsBuilder = CalendarConstraints.Builder().apply {
- setStart(LocalDate.now().toEpochDay())
+ setStart(rangeStart)
+ setEnd(rangeEnd)
+ setValidator(SchoolDaysValidator(rangeStart, rangeEnd))
}
- val datePicker =
- MaterialDatePicker.Builder.datePicker()
- .setCalendarConstraints(constraintsBuilder.build())
- .setSelection(currentDate.toTimestamp())
- .build()
+ val datePicker = MaterialDatePicker.Builder.datePicker()
+ .setCalendarConstraints(constraintsBuilder.build())
+ .setSelection(currentDate.toTimestamp())
+ .build()
datePicker.addOnPositiveButtonClickListener {
date = it.toLocalDateTime().toLocalDate()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt
index 49d094b78..3bbed18b7 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt
@@ -16,7 +16,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
-import io.github.wulkanowy.utils.schoolYearStart
+import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
@@ -112,7 +112,7 @@ class LuckyNumberHistoryFragment :
}
override fun showDatePickerDialog(currentDate: LocalDate) {
- val baseDate = currentDate.schoolYearStart
+ val baseDate = currentDate.firstSchoolDayInSchoolYear
val rangeStart = baseDate.toTimestamp()
val rangeEnd = LocalDate.now().plusWeeks(1).toTimestamp()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
index b49d11c12..dd1136398 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
@@ -24,9 +24,9 @@ import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragme
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
+import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.schoolYearEnd
-import io.github.wulkanowy.utils.schoolYearStart
+import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
@@ -194,9 +194,9 @@ class TimetableFragment : BaseFragment(R.layout.fragme
}
override fun showDatePickerDialog(currentDate: LocalDate) {
- val baseDate = currentDate.schoolYearStart
+ val baseDate = currentDate.firstSchoolDayInSchoolYear
val rangeStart = baseDate.toTimestamp()
- val rangeEnd = LocalDate.now().schoolYearEnd.toTimestamp()
+ val rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear.toTimestamp()
val constraintsBuilder = CalendarConstraints.Builder().apply {
setValidator(SchoolDaysValidator(rangeStart, rangeEnd))
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt
index fdc8b8874..c2ce80289 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsAdapter.kt
@@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.timetable.additional
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.ViewGroup
+import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.databinding.ItemTimetableAdditionalBinding
@@ -14,6 +15,8 @@ class AdditionalLessonsAdapter @Inject constructor() :
var items = emptyList()
+ var onDeleteClickListener: (timetableAdditional: TimetableAdditional) -> Unit = {}
+
override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
@@ -25,8 +28,12 @@ class AdditionalLessonsAdapter @Inject constructor() :
val item = items[position]
with(holder.binding) {
- additionalLessonItemTime.text = "${item.start.toFormattedString("HH:mm")} - ${item.end.toFormattedString("HH:mm")}"
+ additionalLessonItemTime.text =
+ "${item.start.toFormattedString("HH:mm")} - ${item.end.toFormattedString("HH:mm")}"
additionalLessonItemSubject.text = item.subject
+
+ additionalLessonItemDelete.isVisible = item.isAddedByUser
+ additionalLessonItemDelete.setOnClickListener { onDeleteClickListener(item) }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt
index e8fb9e440..b2a4a9a32 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt
@@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.timetable.additional
import android.os.Bundle
import android.view.View
+import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.MaterialDatePicker
@@ -10,13 +11,15 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.databinding.FragmentTimetableAdditionalBinding
import io.github.wulkanowy.ui.base.BaseFragment
+import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
+import io.github.wulkanowy.ui.modules.timetable.additional.add.AdditionalLessonAddDialog
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
+import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.schoolYearEnd
-import io.github.wulkanowy.utils.schoolYearStart
+import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
@@ -53,7 +56,9 @@ class AdditionalLessonsFragment :
override fun initView() {
with(binding.additionalLessonsRecycler) {
layoutManager = LinearLayoutManager(context)
- adapter = additionalLessonsAdapter
+ adapter = additionalLessonsAdapter.apply {
+ onDeleteClickListener = { presenter.onDeleteLessonsSelected(it) }
+ }
addItemDecoration(DividerItemDecoration(context))
}
@@ -61,9 +66,7 @@ class AdditionalLessonsFragment :
additionalLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
additionalLessonsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
additionalLessonsSwipe.setProgressBackgroundColorSchemeColor(
- requireContext().getThemeAttrColor(
- R.attr.colorSwipeRefresh
- )
+ requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)
)
additionalLessonsErrorRetry.setOnClickListener { presenter.onRetry() }
additionalLessonsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
@@ -72,6 +75,8 @@ class AdditionalLessonsFragment :
additionalLessonsNavDate.setOnClickListener { presenter.onPickDate() }
additionalLessonsNextButton.setOnClickListener { presenter.onNextDay() }
+ openAddAdditionalLessonButton.setOnClickListener { presenter.onAdditionalLessonAddButtonClicked() }
+
additionalLessonsNavContainer.elevation = requireContext().dpToPx(8f)
}
}
@@ -90,6 +95,10 @@ class AdditionalLessonsFragment :
}
}
+ override fun showSuccessMessage() {
+ getString(R.string.additional_lessons_delete_success)
+ }
+
override fun updateNavigationDay(date: String) {
binding.additionalLessonsNavDate.text = date
}
@@ -131,21 +140,24 @@ class AdditionalLessonsFragment :
binding.additionalLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
}
+ override fun showAddAdditionalLessonDialog() {
+ (activity as? MainActivity)?.showDialogFragment(AdditionalLessonAddDialog.newInstance())
+ }
+
override fun showDatePickerDialog(currentDate: LocalDate) {
val now = LocalDate.now()
- val startOfSchoolYear = now.schoolYearStart.toTimestamp()
- val endOfSchoolYear = now.schoolYearEnd.toTimestamp()
+ val startOfSchoolYear = now.firstSchoolDayInSchoolYear.toTimestamp()
+ val endOfSchoolYear = now.lastSchoolDayInSchoolYear.toTimestamp()
val constraintsBuilder = CalendarConstraints.Builder().apply {
setValidator(SchoolDaysValidator(startOfSchoolYear, endOfSchoolYear))
setStart(startOfSchoolYear)
setEnd(endOfSchoolYear)
}
- val datePicker =
- MaterialDatePicker.Builder.datePicker()
- .setCalendarConstraints(constraintsBuilder.build())
- .setSelection(currentDate.toTimestamp())
- .build()
+ val datePicker = MaterialDatePicker.Builder.datePicker()
+ .setCalendarConstraints(constraintsBuilder.build())
+ .setSelection(currentDate.toTimestamp())
+ .build()
datePicker.addOnPositiveButtonClickListener {
val date = it.toLocalDateTime()
@@ -157,6 +169,18 @@ class AdditionalLessonsFragment :
}
}
+ override fun showDeleteLessonDialog(timetableAdditional: TimetableAdditional) {
+ AlertDialog.Builder(requireContext())
+ .setTitle(getString(R.string.additional_lessons_delete_title))
+ .setItems(
+ arrayOf(
+ getString(R.string.additional_lessons_delete_one),
+ getString(R.string.additional_lessons_delete_series)
+ )
+ ) { _, position -> presenter.onDeleteDialogSelectItem(position, timetableAdditional) }
+ .show()
+ }
+
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/timetable/additional/AdditionalLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt
index 3496e141a..742a8d592 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt
@@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.timetable.additional
import android.annotation.SuppressLint
import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
@@ -20,6 +21,7 @@ import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
import timber.log.Timber
import java.time.LocalDate
import javax.inject.Inject
@@ -63,6 +65,10 @@ class AdditionalLessonsPresenter @Inject constructor(
view?.showDatePickerDialog(currentDate)
}
+ fun onAdditionalLessonAddButtonClicked() {
+ view?.showAddAdditionalLessonDialog()
+ }
+
fun onDateSet(year: Int, month: Int, day: Int) {
loadData(LocalDate.of(year, month, day))
reloadView()
@@ -98,6 +104,36 @@ class AdditionalLessonsPresenter @Inject constructor(
}.launch("holidays")
}
+ fun onDeleteLessonsSelected(timetableAdditional: TimetableAdditional) {
+ if (timetableAdditional.repeatId == null) {
+ deleteAdditionalLessons(timetableAdditional, false)
+ } else {
+ view?.showDeleteLessonDialog(timetableAdditional)
+ }
+ }
+
+ fun onDeleteDialogSelectItem(position: Int, timetableAdditional: TimetableAdditional) {
+ deleteAdditionalLessons(timetableAdditional, position == 1)
+ }
+
+ private fun deleteAdditionalLessons(
+ timetableAdditional: TimetableAdditional,
+ deleteSeries: Boolean
+ ) {
+ presenterScope.launch {
+ Timber.i("Additional Lesson delete start")
+ runCatching { timetableRepository.deleteAdditional(timetableAdditional, deleteSeries) }
+ .onSuccess {
+ Timber.i("Additional Lesson delete: Success")
+ view?.showSuccessMessage()
+ }
+ .onFailure {
+ Timber.i("Additional Lesson delete result: An exception occurred")
+ errorHandler.dispatch(it)
+ }
+ }
+ }
+
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
currentDate = date
@@ -111,7 +147,7 @@ class AdditionalLessonsPresenter @Inject constructor(
Status.SUCCESS -> {
Timber.i("Loading additional lessons lessons result: Success")
view?.apply {
- updateData(it.data!!.additional.sortedBy { item -> item.date })
+ updateData(it.data!!.additional.sortedBy { item -> item.start })
showEmpty(it.data.additional.isEmpty())
showErrorView(false)
showContent(it.data.additional.isNotEmpty())
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt
index 97eb2ae7b..03466d69d 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt
@@ -35,4 +35,10 @@ interface AdditionalLessonsView : BaseView {
fun showNextButton(show: Boolean)
fun showDatePickerDialog(currentDate: LocalDate)
+
+ fun showAddAdditionalLessonDialog()
+
+ fun showSuccessMessage()
+
+ fun showDeleteLessonDialog(timetableAdditional: TimetableAdditional)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt
new file mode 100644
index 000000000..f57841c9c
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt
@@ -0,0 +1,188 @@
+package io.github.wulkanowy.ui.modules.timetable.additional.add
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.widget.doOnTextChanged
+import com.google.android.material.datepicker.CalendarConstraints
+import com.google.android.material.datepicker.MaterialDatePicker
+import com.google.android.material.timepicker.MaterialTimePicker
+import com.google.android.material.timepicker.TimeFormat
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.databinding.DialogAdditionalAddBinding
+import io.github.wulkanowy.ui.base.BaseDialogFragment
+import io.github.wulkanowy.utils.SchoolDaysValidator
+import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
+import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.utils.toLocalDateTime
+import io.github.wulkanowy.utils.toTimestamp
+import java.time.LocalDate
+import java.time.LocalTime
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class AdditionalLessonAddDialog : BaseDialogFragment(),
+ AdditionalLessonAddView {
+
+ @Inject
+ lateinit var presenter: AdditionalLessonAddPresenter
+
+ companion object {
+ fun newInstance() = AdditionalLessonAddDialog()
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setStyle(STYLE_NO_TITLE, 0)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogAdditionalAddBinding.inflate(inflater).apply { binding = this }.root
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ presenter.onAttachView(this)
+ }
+
+ override fun initView() {
+ with(binding) {
+ additionalLessonDialogStartEdit.doOnTextChanged { _, _, _, _ ->
+ additionalLessonDialogStart.isErrorEnabled = false
+ additionalLessonDialogStart.error = null
+ }
+ additionalLessonDialogEndEdit.doOnTextChanged { _, _, _, _ ->
+ additionalLessonDialogEnd.isErrorEnabled = false
+ additionalLessonDialogEnd.error = null
+ }
+ additionalLessonDialogDateEdit.doOnTextChanged { _, _, _, _ ->
+ additionalLessonDialogDate.isErrorEnabled = false
+ additionalLessonDialogDate.error = null
+ }
+ additionalLessonDialogContentEdit.doOnTextChanged { _, _, _, _ ->
+ additionalLessonDialogContent.isErrorEnabled = false
+ additionalLessonDialogContent.error = null
+ }
+
+ additionalLessonDialogAdd.setOnClickListener {
+ presenter.onAddAdditionalClicked(
+ start = additionalLessonDialogStartEdit.text?.toString(),
+ end = additionalLessonDialogEndEdit.text?.toString(),
+ date = additionalLessonDialogDateEdit.text?.toString(),
+ content = additionalLessonDialogContentEdit.text?.toString(),
+ isRepeat = additionalLessonDialogRepeat.isChecked
+ )
+ }
+ additionalLessonDialogClose.setOnClickListener { dismiss() }
+ additionalLessonDialogDateEdit.setOnClickListener { presenter.showDatePicker() }
+ additionalLessonDialogStartEdit.setOnClickListener { presenter.showStartTimePicker() }
+ additionalLessonDialogEndEdit.setOnClickListener { presenter.showEndTimePicker() }
+ }
+ }
+
+ override fun showSuccessMessage() {
+ showMessage(getString(R.string.additional_lessons_add_success))
+ }
+
+ override fun setErrorDateRequired() {
+ with(binding.additionalLessonDialogDate) {
+ isErrorEnabled = true
+ error = getString(R.string.error_field_required)
+ }
+ }
+
+ override fun setErrorStartRequired() {
+ with(binding.additionalLessonDialogStart) {
+ isErrorEnabled = true
+ error = getString(R.string.error_field_required)
+ }
+ }
+
+ override fun setErrorEndRequired() {
+ with(binding.additionalLessonDialogEnd) {
+ isErrorEnabled = true
+ error = getString(R.string.error_field_required)
+ }
+ }
+
+ override fun setErrorContentRequired() {
+ with(binding.additionalLessonDialogContent) {
+ isErrorEnabled = true
+ error = getString(R.string.error_field_required)
+ }
+ }
+
+ override fun setErrorIncorrectEndTime() {
+ with(binding.additionalLessonDialogEnd) {
+ isErrorEnabled = true
+ error = getString(R.string.additional_lessons_end_time_error)
+ }
+ }
+
+ override fun closeDialog() {
+ dismiss()
+ }
+
+ override fun showDatePickerDialog(selectedDate: LocalDate) {
+ val rangeStart = LocalDate.now().toTimestamp()
+ val rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear.toTimestamp()
+ val constraintsBuilder = CalendarConstraints.Builder().apply {
+ setStart(rangeStart)
+ setEnd(rangeEnd)
+ setValidator(SchoolDaysValidator(rangeStart, rangeEnd))
+ }
+ val datePicker = MaterialDatePicker.Builder.datePicker()
+ .setCalendarConstraints(constraintsBuilder.build())
+ .setSelection(selectedDate.toTimestamp())
+ .build()
+
+ datePicker.addOnPositiveButtonClickListener {
+ val date = it.toLocalDateTime().toLocalDate()
+ presenter.onDateSelected(date)
+ binding.additionalLessonDialogDateEdit.setText(date.toFormattedString())
+ }
+
+ if (!parentFragmentManager.isStateSaved) {
+ datePicker.show(parentFragmentManager, null)
+ }
+ }
+
+ override fun showStartTimePickerDialog(selectedTime: LocalTime) {
+ showTimePickerDialog(selectedTime) {
+ presenter.onStartTimeSelected(it)
+ binding.additionalLessonDialogStartEdit.setText(it.toString())
+ }
+ }
+
+ override fun showEndTimePickerDialog(selectedTime: LocalTime) {
+ showTimePickerDialog(selectedTime) {
+ presenter.onEndTimeSelected(it)
+ binding.additionalLessonDialogEndEdit.setText(it.toString())
+ }
+ }
+
+ private fun showTimePickerDialog(defaultTime: LocalTime, onTimeSelected: (LocalTime) -> Unit) {
+ val timePicker = MaterialTimePicker.Builder()
+ .setTimeFormat(TimeFormat.CLOCK_24H)
+ .setHour(defaultTime.hour)
+ .setMinute(defaultTime.minute)
+ .build()
+
+ timePicker.addOnPositiveButtonClickListener {
+ onTimeSelected(LocalTime.of(timePicker.hour, timePicker.minute))
+ }
+
+ if (!parentFragmentManager.isStateSaved) {
+ timePicker.show(parentFragmentManager, null)
+ }
+ }
+
+ override fun onDestroyView() {
+ presenter.onDetachView()
+ super.onDestroyView()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt
new file mode 100644
index 000000000..7bed5619e
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt
@@ -0,0 +1,166 @@
+package io.github.wulkanowy.ui.modules.timetable.additional.add
+
+import io.github.wulkanowy.data.db.entities.TimetableAdditional
+import io.github.wulkanowy.data.repositories.SemesterRepository
+import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.repositories.TimetableRepository
+import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.base.ErrorHandler
+import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
+import io.github.wulkanowy.utils.toLocalDate
+import kotlinx.coroutines.launch
+import timber.log.Timber
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.LocalTime
+import java.time.temporal.ChronoUnit
+import java.util.UUID
+import javax.inject.Inject
+
+class AdditionalLessonAddPresenter @Inject constructor(
+ errorHandler: ErrorHandler,
+ studentRepository: StudentRepository,
+ private val timetableRepository: TimetableRepository,
+ private val semesterRepository: SemesterRepository
+) : BasePresenter(errorHandler, studentRepository) {
+
+ private var selectedStartTime = LocalTime.of(15, 0)
+
+ private var selectedEndTime = LocalTime.of(15, 45)
+
+ private var selectedDate = LocalDate.now()
+
+ override fun onAttachView(view: AdditionalLessonAddView) {
+ super.onAttachView(view)
+ view.initView()
+ Timber.i("AdditionalLesson details view was initialized")
+ }
+
+ fun showDatePicker() {
+ view?.showDatePickerDialog(selectedDate)
+ }
+
+ fun showStartTimePicker() {
+ view?.showStartTimePickerDialog(selectedStartTime)
+ }
+
+ fun showEndTimePicker() {
+ view?.showEndTimePickerDialog(selectedEndTime)
+ }
+
+ fun onStartTimeSelected(time: LocalTime) {
+ selectedStartTime = time
+ }
+
+ fun onEndTimeSelected(time: LocalTime) {
+ selectedEndTime = time
+ }
+
+ fun onDateSelected(date: LocalDate) {
+ selectedDate = date
+ }
+
+ fun onAddAdditionalClicked(
+ start: String?,
+ end: String?,
+ date: String?,
+ content: String?,
+ isRepeat: Boolean
+ ) {
+ if (isUserInputValid(start, end, date, content)) {
+ addAdditionalLesson(
+ start = LocalTime.parse(start!!),
+ end = LocalTime.parse(end),
+ date = date!!.toLocalDate(),
+ subject = content!!,
+ isRepeat = isRepeat
+ )
+ }
+ }
+
+ private fun isUserInputValid(
+ start: String?,
+ end: String?,
+ date: String?,
+ content: String?
+ ): Boolean {
+ var isValid = true
+
+ if (start.isNullOrBlank()) {
+ view?.setErrorStartRequired()
+ isValid = false
+ }
+
+ if (end.isNullOrBlank()) {
+ view?.setErrorEndRequired()
+ isValid = false
+ }
+
+ if (date.isNullOrBlank()) {
+ view?.setErrorDateRequired()
+ isValid = false
+ }
+
+ if (content.isNullOrBlank()) {
+ view?.setErrorContentRequired()
+ isValid = false
+ }
+
+ if (selectedStartTime >= selectedEndTime) {
+ view?.setErrorIncorrectEndTime()
+ isValid = false
+ }
+
+ return isValid
+ }
+
+ private fun addAdditionalLesson(
+ start: LocalTime,
+ end: LocalTime,
+ date: LocalDate,
+ subject: String,
+ isRepeat: Boolean
+ ) {
+ presenterScope.launch {
+ val semester = runCatching {
+ val student = studentRepository.getCurrentStudent()
+ semesterRepository.getCurrentSemester(student)
+ }
+ .onFailure(errorHandler::dispatch)
+ .getOrNull() ?: return@launch
+
+ val weeks = if (isRepeat) {
+ ChronoUnit.WEEKS.between(date, date.lastSchoolDayInSchoolYear)
+ } else 0
+ val uniqueRepeatId = UUID.randomUUID().takeIf { isRepeat }
+
+ val lessonsToAdd = (0..weeks).map {
+ TimetableAdditional(
+ studentId = semester.studentId,
+ diaryId = semester.diaryId,
+ start = LocalDateTime.of(date, start),
+ end = LocalDateTime.of(date, end),
+ date = date.plusWeeks(it),
+ subject = subject
+ ).apply {
+ isAddedByUser = true
+ repeatId = uniqueRepeatId
+ }
+ }
+
+ Timber.i("AdditionalLesson insert start")
+ runCatching { timetableRepository.saveAdditionalList(lessonsToAdd) }
+ .onSuccess {
+ Timber.i("AdditionalLesson insert: Success")
+ view?.run {
+ showSuccessMessage()
+ closeDialog()
+ }
+ }
+ .onFailure {
+ Timber.i("AdditionalLesson insert result: An exception occurred")
+ errorHandler.dispatch(it)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddView.kt
new file mode 100644
index 000000000..0df53815b
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddView.kt
@@ -0,0 +1,30 @@
+package io.github.wulkanowy.ui.modules.timetable.additional.add
+
+import io.github.wulkanowy.ui.base.BaseView
+import java.time.LocalDate
+import java.time.LocalTime
+
+interface AdditionalLessonAddView : BaseView {
+
+ fun initView()
+
+ fun closeDialog()
+
+ fun showDatePickerDialog(selectedDate: LocalDate)
+
+ fun showStartTimePickerDialog(selectedTime: LocalTime)
+
+ fun showEndTimePickerDialog(selectedTime: LocalTime)
+
+ fun showSuccessMessage()
+
+ fun setErrorDateRequired()
+
+ fun setErrorStartRequired()
+
+ fun setErrorEndRequired()
+
+ fun setErrorContentRequired()
+
+ fun setErrorIncorrectEndTime()
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt
index a6b126447..4d5e3e1a5 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt
@@ -18,10 +18,10 @@ import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
+import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
import io.github.wulkanowy.utils.getCompatDrawable
import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.schoolYearEnd
-import io.github.wulkanowy.utils.schoolYearStart
+import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
@@ -68,9 +68,7 @@ class CompletedLessonsFragment :
completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
completedLessonsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
completedLessonsSwipe.setProgressBackgroundColorSchemeColor(
- requireContext().getThemeAttrColor(
- R.attr.colorSwipeRefresh
- )
+ requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)
)
completedLessonErrorRetry.setOnClickListener { presenter.onRetry() }
completedLessonErrorDetails.setOnClickListener { presenter.onDetailsClick() }
@@ -154,19 +152,18 @@ class CompletedLessonsFragment :
override fun showDatePickerDialog(currentDate: LocalDate) {
val now = LocalDate.now()
- val startOfSchoolYear = now.schoolYearStart.toTimestamp()
- val endOfSchoolYear = now.schoolYearEnd.toTimestamp()
+ val startOfSchoolYear = now.firstSchoolDayInSchoolYear.toTimestamp()
+ val endOfSchoolYear = now.lastSchoolDayInSchoolYear.toTimestamp()
val constraintsBuilder = CalendarConstraints.Builder().apply {
setValidator(SchoolDaysValidator(startOfSchoolYear, endOfSchoolYear))
setStart(startOfSchoolYear)
setEnd(endOfSchoolYear)
}
- val datePicker =
- MaterialDatePicker.Builder.datePicker()
- .setCalendarConstraints(constraintsBuilder.build())
- .setSelection(currentDate.toTimestamp())
- .build()
+ val datePicker = MaterialDatePicker.Builder.datePicker()
+ .setCalendarConstraints(constraintsBuilder.build())
+ .setSelection(currentDate.toTimestamp())
+ .build()
datePicker.addOnPositiveButtonClickListener {
val date = it.toLocalDateTime()
diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt
index 94b6a2191..bebb6c541 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt
@@ -85,35 +85,31 @@ inline val LocalDate.previousOrSameSchoolDay: LocalDate
inline val LocalDate.weekDayName: String
get() = format(DateTimeFormatter.ofPattern("EEEE", Locale.getDefault()))
-inline val LocalDate.monday: LocalDate
- get() = with(MONDAY)
+inline val LocalDate.monday: LocalDate get() = with(MONDAY)
-inline val LocalDate.sunday: LocalDate
- get() = with(SUNDAY)
+inline val LocalDate.sunday: LocalDate get() = with(SUNDAY)
/**
* [Dz.U. 2016 poz. 1335](http://prawo.sejm.gov.pl/isap.nsf/DocDetails.xsp?id=WDU20160001335)
*/
-inline val LocalDate.isHolidays: Boolean
- get() = isBefore(firstSchoolDay) && isAfter(lastSchoolDay)
+val LocalDate.isHolidays: Boolean
+ get() = isBefore(firstSchoolDayInCalendarYear) && isAfter(lastSchoolDayInCalendarYear)
-inline val LocalDate.firstSchoolDay: LocalDate
- get() = LocalDate.of(year, 9, 1).run {
- when (dayOfWeek) {
- FRIDAY, SATURDAY, SUNDAY -> with(firstInMonth(MONDAY))
- else -> this
- }
+val LocalDate.firstSchoolDayInSchoolYear: LocalDate
+ get() = withYear(if (this.monthValue <= 6) this.year - 1 else this.year).firstSchoolDayInCalendarYear
+
+val LocalDate.lastSchoolDayInSchoolYear: LocalDate
+ get() = withYear(if (this.monthValue > 6) this.year + 1 else this.year).lastSchoolDayInCalendarYear
+
+fun LocalDate.getLastSchoolDayIfHoliday(schoolYear: Int): LocalDate {
+ val date = LocalDate.of(schoolYear.getSchoolYearByMonth(monthValue), monthValue, dayOfMonth)
+
+ if (date.isHolidays) {
+ return date.lastSchoolDayInCalendarYear
}
-inline val LocalDate.lastSchoolDay: LocalDate
- get() = LocalDate.of(year, 6, 20)
- .with(next(FRIDAY))
-
-inline val LocalDate.schoolYearStart: LocalDate
- get() = withYear(if (this.monthValue <= 6) this.year - 1 else this.year).firstSchoolDay
-
-inline val LocalDate.schoolYearEnd: LocalDate
- get() = withYear(if (this.monthValue > 6) this.year + 1 else this.year).lastSchoolDay
+ return date
+}
private fun Int.getSchoolYearByMonth(monthValue: Int): Int {
return when (monthValue) {
@@ -122,12 +118,15 @@ private fun Int.getSchoolYearByMonth(monthValue: Int): Int {
}
}
-fun LocalDate.getLastSchoolDayIfHoliday(schoolYear: Int): LocalDate {
- val date = LocalDate.of(schoolYear.getSchoolYearByMonth(monthValue), monthValue, dayOfMonth)
-
- if (date.isHolidays) {
- return date.lastSchoolDay
+private inline val LocalDate.firstSchoolDayInCalendarYear: LocalDate
+ get() = LocalDate.of(year, 9, 1).run {
+ when (dayOfWeek) {
+ FRIDAY, SATURDAY, SUNDAY -> with(firstInMonth(MONDAY))
+ else -> this
+ }
}
- return date
-}
+private inline val LocalDate.lastSchoolDayInCalendarYear: LocalDate
+ get() = LocalDate.of(year, 6, 20)
+ .with(next(FRIDAY))
+
diff --git a/app/src/main/res/drawable/ic_all_clock.xml b/app/src/main/res/drawable/ic_all_clock.xml
new file mode 100644
index 000000000..4b98ed233
--- /dev/null
+++ b/app/src/main/res/drawable/ic_all_clock.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_calendat_all.xml b/app/src/main/res/drawable/ic_calendat_all.xml
new file mode 100644
index 000000000..5908035ed
--- /dev/null
+++ b/app/src/main/res/drawable/ic_calendat_all.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/layout/dialog_additional_add.xml b/app/src/main/res/layout/dialog_additional_add.xml
new file mode 100644
index 000000000..884018e55
--- /dev/null
+++ b/app/src/main/res/layout/dialog_additional_add.xml
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_grade.xml b/app/src/main/res/layout/dialog_grade.xml
index 9ed2bc068..9c52c1d0b 100644
--- a/app/src/main/res/layout/dialog_grade.xml
+++ b/app/src/main/res/layout/dialog_grade.xml
@@ -204,7 +204,6 @@
android:insetBottom="0dp"
android:minWidth="88dp"
android:text="@string/all_close" />
-
diff --git a/app/src/main/res/layout/dialog_homework_add.xml b/app/src/main/res/layout/dialog_homework_add.xml
index b9b8d1a2e..74c583aa1 100644
--- a/app/src/main/res/layout/dialog_homework_add.xml
+++ b/app/src/main/res/layout/dialog_homework_add.xml
@@ -5,8 +5,8 @@
android:layout_height="match_parent"
android:fillViewport="true"
android:minWidth="300dp"
- android:paddingStart="24dp"
- android:paddingEnd="24dp">
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp">
+ app:startIconDrawable="@drawable/ic_calendat_all">
@@ -66,7 +66,7 @@
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="0dp"
+ android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"
android:hint="@string/all_teacher">
@@ -81,7 +81,7 @@
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="0dp"
+ android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:hint="@string/all_content">
@@ -105,13 +105,12 @@
android:layout_gravity="center_vertical"
android:layout_marginStart="0dp"
android:layout_marginLeft="0dp"
- android:layout_marginTop="8dp"
- android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:insetLeft="0dp"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
+ android:minWidth="88dp"
android:text="@string/all_close"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
@@ -126,6 +125,7 @@
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
+ android:minWidth="88dp"
android:text="@string/all_add"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
diff --git a/app/src/main/res/layout/fragment_timetable_additional.xml b/app/src/main/res/layout/fragment_timetable_additional.xml
index a71f7545b..ec25f9a07 100644
--- a/app/src/main/res/layout/fragment_timetable_additional.xml
+++ b/app/src/main/res/layout/fragment_timetable_additional.xml
@@ -26,6 +26,8 @@
android:id="@+id/additionalLessonsRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:paddingBottom="64dp"
tools:listitem="@layout/item_timetable_additional" />
@@ -108,6 +110,18 @@
android:text="@string/all_retry" />
+
+
+ tools:maxLines="2"
+ tools:text="@tools:sample/lorem/random" />
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 97c8f0345..dff5babfd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -198,6 +198,17 @@
Additional lessons
Show additional lessons
No info about additional lessons
+ New lesson
+ New additional lesson
+ Additional lesson added successfully
+ Additional lesson deleted successfully
+ Repeat weekly
+ Delete additional lesson
+ Just this lesson
+ All in the series
+ Start time
+ End time
+ End time must be greater than start time
diff --git a/build.gradle b/build.gradle
index bd74bab41..9de28f819 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
buildscript {
ext {
- kotlin_version = '1.6.0'
+ kotlin_version = '1.6.10'
about_libraries = '8.9.4'
hilt_version = "2.40.5"
}
From e26860ea5a87cc0fc19f1694d7d9252d33d8b5e3 Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Thu, 23 Dec 2021 13:58:26 +0100
Subject: [PATCH 004/669] Fix state restoring in GradeStatistics (#1667)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Mikołaj Pich
---
.../statistics/GradeStatisticsFragment.kt | 11 ++++--
.../statistics/GradeStatisticsPresenter.kt | 39 ++++++++++++-------
.../grade/statistics/GradeStatisticsView.kt | 2 +-
3 files changed, 35 insertions(+), 17 deletions(-)
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 35d007749..2af59c011 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
@@ -33,6 +33,7 @@ class GradeStatisticsFragment :
companion object {
private const val SAVED_CHART_TYPE = "CURRENT_TYPE"
+ private const val SAVED_SUBJECT_NAME = "SUBJECT_NAME"
fun newInstance() = GradeStatisticsFragment()
}
@@ -46,8 +47,9 @@ class GradeStatisticsFragment :
binding = FragmentGradeStatisticsBinding.bind(view)
messageContainer = binding.gradeStatisticsRecycler
presenter.onAttachView(
- this,
- savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType
+ view = this,
+ type = savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType,
+ subjectName = savedInstanceState?.getSerializable(SAVED_SUBJECT_NAME) as? String,
)
}
@@ -56,6 +58,7 @@ class GradeStatisticsFragment :
with(binding.gradeStatisticsRecycler) {
layoutManager = LinearLayoutManager(requireContext())
+ statisticsAdapter.currentDataType = presenter.currentType
adapter = statisticsAdapter
}
@@ -81,7 +84,8 @@ class GradeStatisticsFragment :
}
}
- override fun updateSubjects(data: ArrayList) {
+ override fun updateSubjects(data: List, selectedIndex: Int) {
+ binding.gradeStatisticsSubjects.setSelection(selectedIndex)
with(subjectsAdapter) {
clear()
addAll(data)
@@ -161,6 +165,7 @@ class GradeStatisticsFragment :
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putSerializable(SAVED_CHART_TYPE, presenter.currentType)
+ outState.putSerializable(SAVED_SUBJECT_NAME, presenter.currentSubjectName)
}
override fun onDestroyView() {
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 53eccad65..e536f473a 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
@@ -31,16 +31,22 @@ class GradeStatisticsPresenter @Inject constructor(
private var currentSemesterId = 0
- private var currentSubjectName: String = "Wszystkie"
+ var currentSubjectName: String = "Wszystkie"
+ private set
private lateinit var lastError: Throwable
var currentType: GradeStatisticsItem.DataType = GradeStatisticsItem.DataType.PARTIAL
private set
- fun onAttachView(view: GradeStatisticsView, type: GradeStatisticsItem.DataType?) {
+ fun onAttachView(
+ view: GradeStatisticsView,
+ type: GradeStatisticsItem.DataType?,
+ subjectName: String?
+ ) {
super.onAttachView(view)
currentType = type ?: GradeStatisticsItem.DataType.PARTIAL
+ currentSubjectName = subjectName ?: currentSubjectName
view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError
}
@@ -127,12 +133,17 @@ class GradeStatisticsPresenter @Inject constructor(
when (it.status) {
Status.LOADING -> Timber.i("Loading grade stats subjects started")
Status.SUCCESS -> {
- subjects = it.data!!
-
+ subjects = requireNotNull(it.data)
Timber.i("Loading grade stats subjects result: Success")
+
view?.run {
- view?.updateSubjects(ArrayList(it.data.map { subject -> subject.name }))
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
+ updateSubjects(
+ data = it.data.map { subject -> subject.name },
+ selectedIndex = it.data.indexOfFirst { subject ->
+ subject.name == currentSubjectName
+ },
+ )
}
}
Status.ERROR -> {
@@ -151,9 +162,11 @@ class GradeStatisticsPresenter @Inject constructor(
) {
Timber.i("Loading grade stats data started")
- currentSubjectName =
- if (preferencesRepository.showAllSubjectsOnStatisticsList) "Wszystkie" else subjectName
currentType = type
+ currentSubjectName = when {
+ preferencesRepository.showAllSubjectsOnStatisticsList -> "Wszystkie"
+ else -> subjectName
+ }
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
@@ -200,9 +213,9 @@ class GradeStatisticsPresenter @Inject constructor(
showRefresh(true)
showProgress(false)
updateData(
- if (isNoContent) emptyList() else it.data!!,
- preferencesRepository.gradeColorTheme,
- preferencesRepository.showAllSubjectsOnStatisticsList
+ newItems = if (isNoContent) emptyList() else it.data!!,
+ newTheme = preferencesRepository.gradeColorTheme,
+ showAllSubjectsOnStatisticsList = preferencesRepository.showAllSubjectsOnStatisticsList,
)
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
}
@@ -215,9 +228,9 @@ class GradeStatisticsPresenter @Inject constructor(
showEmpty(isNoContent)
showErrorView(false)
updateData(
- if (isNoContent) emptyList() else it.data,
- preferencesRepository.gradeColorTheme,
- preferencesRepository.showAllSubjectsOnStatisticsList
+ newItems = if (isNoContent) emptyList() else it.data,
+ newTheme = preferencesRepository.gradeColorTheme,
+ showAllSubjectsOnStatisticsList = preferencesRepository.showAllSubjectsOnStatisticsList,
)
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
}
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 8e9a206bc..4333bb0a9 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
@@ -12,7 +12,7 @@ interface GradeStatisticsView : BaseView {
fun initView()
- fun updateSubjects(data: ArrayList)
+ fun updateSubjects(data: List, selectedIndex: Int)
fun updateData(
newItems: List,
From 497083be97dafd11821498f054fe8686dadcf72e Mon Sep 17 00:00:00 2001
From: Mateusz Idziejczak
Date: Sat, 25 Dec 2021 06:46:24 +0100
Subject: [PATCH 005/669] Update timetable to next day if there is no more
lessons today (#1551)
---
.idea/codeStyles/Project.xml | 15 -----
.../timetablewidget/TimetableWidgetFactory.kt | 12 ++++
.../TimetableWidgetProvider.kt | 56 +++++++++++--------
3 files changed, 46 insertions(+), 37 deletions(-)
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index ab7844747..1f93faefd 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -2,14 +2,6 @@
-
-
-
-
@@ -126,13 +118,6 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
index f3d760f09..51b790e88 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
@@ -22,12 +22,14 @@ import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getCurrentThemeWidgetKey
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
+import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getTodayLastLessonEndDateTimeWidgetKey
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.toFirstResult
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.runBlocking
import timber.log.Timber
import java.time.LocalDate
+import java.time.ZoneOffset
class TimetableWidgetFactory(
private val timetableRepository: TimetableRepository,
@@ -70,6 +72,16 @@ class TimetableWidgetFactory(
updateTheme(appWidgetId)
lessons = getLessons(date, studentId)
+
+ if (date == LocalDate.now()) {
+ val todayLastLessonEndTimestamp =
+ lessons.maxOf { it.end }.toEpochSecond(ZoneOffset.UTC)
+ sharedPref.putLong(
+ getTodayLastLessonEndDateTimeWidgetKey(appWidgetId),
+ todayLastLessonEndTimestamp,
+ true
+ )
+ }
}
}
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 0f0691160..641d22612 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
@@ -3,10 +3,7 @@ package io.github.wulkanowy.ui.modules.timetablewidget
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
-import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED
-import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
-import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
-import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
+import android.appwidget.AppWidgetManager.*
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
@@ -25,22 +22,14 @@ import io.github.wulkanowy.services.HiltBroadcastReceiver
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity
-import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.PendingIntentCompat
-import io.github.wulkanowy.utils.capitalise
-import io.github.wulkanowy.utils.createNameInitialsDrawable
-import io.github.wulkanowy.utils.getCompatColor
-import io.github.wulkanowy.utils.nextOrSameSchoolDay
-import io.github.wulkanowy.utils.nextSchoolDay
-import io.github.wulkanowy.utils.nickOrName
-import io.github.wulkanowy.utils.previousSchoolDay
-import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import timber.log.Timber
import java.time.LocalDate
-import java.time.LocalDate.now
+import java.time.LocalDateTime
+import java.time.ZoneOffset
import javax.inject.Inject
@AndroidEntryPoint
@@ -76,6 +65,9 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
fun getDateWidgetKey(appWidgetId: Int) = "timetable_widget_date_$appWidgetId"
+ fun getTodayLastLessonEndDateTimeWidgetKey(appWidgetId: Int) =
+ "timetable_widget_today_last_lesson_end_date_time_$appWidgetId"
+
fun getStudentWidgetKey(appWidgetId: Int) = "timetable_widget_student_$appWidgetId"
fun getThemeWidgetKey(appWidgetId: Int) = "timetable_widget_theme_$appWidgetId"
@@ -100,7 +92,8 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId ->
val student =
getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)
- updateWidget(context, appWidgetId, now().nextOrSameSchoolDay, student)
+
+ updateWidget(context, appWidgetId, getWidgetDateToLoad(appWidgetId), student)
}
} else {
val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE)
@@ -112,15 +105,17 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
val savedDate =
LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0))
val date = when (buttonType) {
- BUTTON_RESET -> now().nextOrSameSchoolDay
+ BUTTON_RESET -> getWidgetDateToLoad(toggledWidgetId)
BUTTON_NEXT -> savedDate.nextSchoolDay
BUTTON_PREV -> savedDate.previousSchoolDay
- else -> now().nextOrSameSchoolDay
+ else -> getWidgetDateToLoad(toggledWidgetId)
+ }
+ if (!buttonType.isNullOrBlank()) {
+ analytics.logEvent(
+ "changed_timetable_widget_day",
+ "button" to buttonType
+ )
}
- if (!buttonType.isNullOrBlank()) analytics.logEvent(
- "changed_timetable_widget_day",
- "button" to buttonType
- )
updateWidget(context, toggledWidgetId, date, student)
}
}
@@ -278,4 +273,21 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
avatarDrawable.draw(canvas)
return avatarBitmap
}
+
+ private fun getWidgetDateToLoad(appWidgetId: Int): LocalDate {
+ val lastLessonEndTimestamp =
+ sharedPref.getLong(getTodayLastLessonEndDateTimeWidgetKey(appWidgetId), 0)
+ val lastLessonEndDateTime =
+ LocalDateTime.ofEpochSecond(lastLessonEndTimestamp, 0, ZoneOffset.UTC)
+
+ val todayDate = LocalDate.now()
+ val isLastLessonEndDateNow = lastLessonEndDateTime.toLocalDate() == todayDate
+ val isLastLessonEndDateAfterNowTime = LocalDateTime.now() > lastLessonEndDateTime
+
+ return if (isLastLessonEndDateNow && isLastLessonEndDateAfterNowTime) {
+ todayDate.nextSchoolDay
+ } else {
+ todayDate.nextOrSameSchoolDay
+ }
+ }
}
From 65f114ce05233582ab2d1c57d264c7f32239f35b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 25 Dec 2021 05:54:17 +0000
Subject: [PATCH 006/669] Bump kotlinx-serialization-json from 1.3.1 to 1.3.2
(#1733)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 1ff8b9268..efbe536cc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -178,7 +178,7 @@ dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
- implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1"
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
implementation "androidx.core:core-ktx:1.7.0"
From cd12c4c89145b7fad1ac5fd775ce47dbf94a8d83 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 25 Dec 2021 05:54:34 +0000
Subject: [PATCH 007/669] Bump agconnect-crash from 1.6.2.300 to 1.6.3.200
(#1732)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index efbe536cc..77c47fe0b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -238,7 +238,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:20.5.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.3.2.300'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.2.300'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.3.200'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From f718147ae9b6a81edc79eaca255f027a0ab2f770 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 25 Dec 2021 05:55:01 +0000
Subject: [PATCH 008/669] Bump agcp from 1.6.2.300 to 1.6.3.200 (#1730)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 9de28f819..b06af2e16 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.0.4'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
- classpath 'com.huawei.agconnect:agcp:1.6.2.300'
+ classpath 'com.huawei.agconnect:agcp:1.6.3.200'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.0"
From 8560fd7e81f1230558feca872ca9a783ef99c692 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 25 Dec 2021 08:38:27 +0000
Subject: [PATCH 009/669] Bump coroutines from 1.5.2 to 1.6.0 (#1731)
---
app/build.gradle | 2 +-
.../io/github/wulkanowy/utils/FlowUtilsKtTest.kt | 12 +++++-------
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 77c47fe0b..135863562 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -170,7 +170,7 @@ ext {
room = "2.4.0"
chucker = "3.5.2"
mockk = "1.12.1"
- coroutines = "1.5.2"
+ coroutines = "1.6.0"
}
dependencies {
diff --git a/app/src/test/java/io/github/wulkanowy/utils/FlowUtilsKtTest.kt b/app/src/test/java/io/github/wulkanowy/utils/FlowUtilsKtTest.kt
index 375a24038..57045a29d 100644
--- a/app/src/test/java/io/github/wulkanowy/utils/FlowUtilsKtTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/utils/FlowUtilsKtTest.kt
@@ -1,23 +1,21 @@
package io.github.wulkanowy.utils
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerifyOrder
-import io.mockk.just
-import io.mockk.mockk
+import io.mockk.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceTimeBy
import org.junit.Test
import kotlin.test.assertEquals
@OptIn(ExperimentalCoroutinesApi::class)
class FlowUtilsKtTest {
- private val testScope = TestCoroutineScope()
+ private val testScope = TestScope(UnconfinedTestDispatcher())
@Test
fun `fetch from two places with same remote data`() {
From 2eee50ad815ed920a19c10b1e5c24baeff8811b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Mon, 27 Dec 2021 07:58:57 +0100
Subject: [PATCH 010/669] Replace view pager in login activity with simple
fragment transactions (#1686)
---
.../ui/modules/login/LoginActivity.kt | 134 ++++++------------
.../wulkanowy/ui/modules/login/LoginData.kt | 9 ++
.../ui/modules/login/LoginErrorHandler.kt | 5 +-
.../ui/modules/login/LoginPresenter.kt | 55 +------
.../wulkanowy/ui/modules/login/LoginView.kt | 11 --
.../login/advanced/LoginAdvancedFragment.kt | 17 +--
.../login/advanced/LoginAdvancedPresenter.kt | 26 +++-
.../login/advanced/LoginAdvancedView.kt | 5 +-
.../modules/login/form/LoginFormFragment.kt | 20 +--
.../modules/login/form/LoginFormPresenter.kt | 6 +-
.../ui/modules/login/form/LoginFormView.kt | 5 +-
.../login/recover/LoginRecoverFragment.kt | 4 +-
.../login/recover/RecoverErrorHandler.kt | 5 +-
.../LoginStudentSelectFragment.kt | 27 ++--
.../LoginStudentSelectPresenter.kt | 19 +--
.../login/symbol/LoginSymbolFragment.kt | 24 ++--
.../login/symbol/LoginSymbolPresenter.kt | 54 +++----
.../modules/login/symbol/LoginSymbolView.kt | 2 +-
.../settings/advanced/AdvancedFragment.kt | 4 -
app/src/main/res/layout/activity_login.xml | 11 +-
.../res/layout/fragment_login_recover.xml | 2 +-
.../ui/modules/login/LoginPresenterTest.kt | 57 --------
.../LoginStudentSelectPresenterTest.kt | 2 +-
23 files changed, 186 insertions(+), 318 deletions(-)
create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt
delete mode 100644 app/src/test/java/io/github/wulkanowy/ui/modules/login/LoginPresenterTest.kt
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 e607cef15..d7d77f73d 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
@@ -4,18 +4,19 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.commit
import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.ActivityLoginBinding
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.recover.LoginRecoverFragment
import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
import io.github.wulkanowy.utils.UpdateHelper
-import io.github.wulkanowy.utils.setOnSelectPageListener
import javax.inject.Inject
@AndroidEntryPoint
@@ -24,21 +25,10 @@ class LoginActivity : BaseActivity(), Logi
@Inject
override lateinit var presenter: LoginPresenter
- private val pagerAdapter by lazy {
- BaseFragmentPagerAdapter(
- fragmentManager = supportFragmentManager,
- pagesCount = 5,
- lifecycle = lifecycle,
- )
- }
-
@Inject
lateinit var updateHelper: UpdateHelper
- override val currentViewIndex get() = binding.loginViewpager.currentItem
-
companion object {
-
fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java)
}
@@ -51,6 +41,50 @@ class LoginActivity : BaseActivity(), Logi
presenter.onAttachView(this)
updateHelper.checkAndInstallUpdates(this)
+
+ if (savedInstanceState == null) {
+ openFragment(LoginFormFragment.newInstance(), clearBackStack = true)
+ }
+ }
+
+ override fun initView() {
+ with(requireNotNull(supportActionBar)) {
+ setDisplayHomeAsUpEnabled(true)
+ setDisplayShowTitleEnabled(false)
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home) onBackPressed()
+ return true
+ }
+
+ fun showActionBar(show: Boolean) {
+ supportActionBar?.run { if (show) show() else hide() }
+ }
+
+ fun navigateToSymbolFragment(loginData: LoginData) {
+ openFragment(LoginSymbolFragment.newInstance(loginData))
+ }
+
+ fun navigateToStudentSelect(studentsWithSemesters: List) {
+ openFragment(LoginStudentSelectFragment.newInstance(studentsWithSemesters))
+ }
+
+ fun onAdvancedLoginClick() {
+ openFragment(LoginAdvancedFragment.newInstance())
+ }
+
+ fun onRecoverClick() {
+ openFragment(LoginRecoverFragment.newInstance())
+ }
+
+ private fun openFragment(fragment: Fragment, clearBackStack: Boolean = false) {
+ supportFragmentManager.commit {
+ replace(R.id.loginContainer, fragment)
+ setReorderingAllowed(true)
+ if (!clearBackStack) addToBackStack(fragment::class.java.name)
+ }
}
override fun onResume() {
@@ -64,78 +98,4 @@ class LoginActivity : BaseActivity(), Logi
super.onActivityResult(requestCode, resultCode, data)
updateHelper.onActivityResult(requestCode, resultCode)
}
-
- override fun initView() {
- with(requireNotNull(supportActionBar)) {
- setDisplayHomeAsUpEnabled(true)
- setDisplayShowTitleEnabled(false)
- }
-
- with(binding.loginViewpager) {
- adapter = pagerAdapter
- isUserInputEnabled = false
- offscreenPageLimit = 2
- setOnSelectPageListener(presenter::onViewSelected)
- }
-
- with(pagerAdapter) {
- containerId = binding.loginViewpager.id
- itemFactory = {
- when (it) {
- 0 -> LoginFormFragment.newInstance()
- 1 -> LoginSymbolFragment.newInstance()
- 2 -> LoginStudentSelectFragment.newInstance()
- 3 -> LoginAdvancedFragment.newInstance()
- 4 -> LoginRecoverFragment.newInstance()
- else -> throw IllegalStateException()
- }
- }
- }
- }
-
- override fun onOptionsItemSelected(item: MenuItem): Boolean {
- if (item.itemId == android.R.id.home) onBackPressed()
- return true
- }
-
- override fun switchView(index: Int) {
- binding.loginViewpager.setCurrentItem(index, false)
- }
-
- override fun showActionBar(show: Boolean) {
- supportActionBar?.run { if (show) show() else hide() }
- }
-
- override fun onBackPressed() {
- presenter.onBackPressed { super.onBackPressed() }
- }
-
- override fun notifyInitSymbolFragment(loginData: Triple) {
- (pagerAdapter.getFragmentInstance(1) as? LoginSymbolFragment)
- ?.onParentInitSymbolFragment(loginData)
- }
-
- override fun notifyInitStudentSelectFragment(studentsWithSemesters: List) {
- (pagerAdapter.getFragmentInstance(2) as? LoginStudentSelectFragment)
- ?.onParentInitStudentSelectFragment(studentsWithSemesters)
- }
-
- fun onFormFragmentAccountLogged(
- studentsWithSemesters: List,
- loginData: Triple
- ) {
- presenter.onFormViewAccountLogged(studentsWithSemesters, loginData)
- }
-
- fun onSymbolFragmentAccountLogged(studentsWithSemesters: List) {
- presenter.onSymbolViewAccountLogged(studentsWithSemesters)
- }
-
- fun onAdvancedLoginClick() {
- presenter.onAdvancedLoginClick()
- }
-
- fun onRecoverClick() {
- presenter.onRecoverClick()
- }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt
new file mode 100644
index 000000000..5d4743589
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginData.kt
@@ -0,0 +1,9 @@
+package io.github.wulkanowy.ui.modules.login
+
+import java.io.Serializable
+
+data class LoginData(
+ val login: String,
+ val password: String,
+ val baseUrl: String,
+) : Serializable
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 ea7215cea..37ab71dce 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
@@ -12,8 +12,9 @@ import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
import io.github.wulkanowy.ui.base.ErrorHandler
import javax.inject.Inject
-class LoginErrorHandler @Inject constructor(@ApplicationContext context: Context) :
- ErrorHandler(context) {
+class LoginErrorHandler @Inject constructor(
+ @ApplicationContext context: Context,
+) : ErrorHandler(context) {
var onBadCredentials: (String?) -> Unit = {}
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 aa1e7eced..9031cb8ab 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
@@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.login
-import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
@@ -14,59 +13,7 @@ class LoginPresenter @Inject constructor(
override fun onAttachView(view: LoginView) {
super.onAttachView(view)
- with(view) {
- initView()
- showActionBar(false)
- }
+ view.initView()
Timber.i("Login view was initialized")
}
-
- fun onFormViewAccountLogged(studentsWithSemesters: List, loginData: Triple) {
- view?.apply {
- if (studentsWithSemesters.isEmpty()) {
- Timber.i("Switch to symbol form")
- notifyInitSymbolFragment(loginData)
- switchView(1)
- } else {
- Timber.i("Switch to student select")
- notifyInitStudentSelectFragment(studentsWithSemesters)
- switchView(2)
- }
- }
- }
-
- fun onSymbolViewAccountLogged(studentsWithSemesters: List) {
- view?.apply {
- Timber.i("Switch to student select")
- notifyInitStudentSelectFragment(studentsWithSemesters)
- switchView(2)
- }
- }
-
- fun onAdvancedLoginClick() {
- view?.switchView(3)
- }
-
- fun onRecoverClick() {
- view?.switchView(4)
- }
-
- fun onViewSelected(index: Int) {
- view?.apply {
- when (index) {
- 0 -> showActionBar(false)
- 1, 2, 3, 4 -> showActionBar(true)
- }
- }
- }
-
- fun onBackPressed(default: () -> Unit) {
- Timber.i("Back pressed in login view")
- view?.apply {
- when (currentViewIndex) {
- 1, 2, 3, 4 -> switchView(0)
- else -> default()
- }
- }
- }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt
index 2a5cf316a..a0949e6d9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginView.kt
@@ -1,19 +1,8 @@
package io.github.wulkanowy.ui.modules.login
-import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
interface LoginView : BaseView {
- val currentViewIndex: Int
-
fun initView()
-
- fun switchView(index: Int)
-
- fun showActionBar(show: Boolean)
-
- fun notifyInitSymbolFragment(loginData: Triple)
-
- fun notifyInitStudentSelectFragment(studentsWithSemesters: List)
}
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 bc29cd146..37dcb38b3 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
@@ -13,6 +13,7 @@ import io.github.wulkanowy.databinding.FragmentLoginAdvancedBinding
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.LoginData
import io.github.wulkanowy.ui.modules.login.form.LoginSymbolAdapter
import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.setOnEditorDoneSignIn
@@ -80,6 +81,8 @@ class LoginAdvancedFragment :
}
override fun initView() {
+ (requireActivity() as LoginActivity).showActionBar(true)
+
hostKeys = resources.getStringArray(R.array.hosts_keys)
hostValues = resources.getStringArray(R.array.hosts_values)
hostSymbols = resources.getStringArray(R.array.hosts_symbols)
@@ -320,14 +323,12 @@ class LoginAdvancedFragment :
binding.loginFormContainer.visibility = if (show) VISIBLE else GONE
}
- override fun notifyParentAccountLogged(studentsWithSemesters: List) {
- (activity as? LoginActivity)?.onFormFragmentAccountLogged(
- studentsWithSemesters, Triple(
- binding.loginFormUsername.text.toString(),
- binding.loginFormPass.text.toString(),
- resources.getStringArray(R.array.hosts_values)[1]
- )
- )
+ override fun navigateToSymbol(loginData: LoginData) {
+ (activity as? LoginActivity)?.navigateToSymbolFragment(loginData)
+ }
+
+ override fun navigateToStudentSelect(studentsWithSemesters: List) {
+ (activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters)
}
override fun onResume() {
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 17d8c5ecb..3543a3041 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
@@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading
@@ -77,7 +78,9 @@ class LoginAdvancedPresenter @Inject constructor(
clearPassError()
clearUsernameError()
if (formHostValue.contains("fakelog")) {
- setDefaultCredentials("jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999")
+ setDefaultCredentials(
+ "jan@fakelog.cf", "jan123", "powiatwulkanowy", "FK100000", "999999"
+ )
}
setSymbol(formHostSymbol)
updateUsernameLabel()
@@ -136,12 +139,21 @@ class LoginAdvancedPresenter @Inject constructor(
}
Status.SUCCESS -> {
Timber.i("Login result: Success")
- analytics.logEvent("registration_form",
+ analytics.logEvent(
+ "registration_form",
"success" to true,
"students" to it.data!!.size,
"error" to "No error"
)
- view?.notifyParentAccountLogged(it.data)
+ val loginData = LoginData(
+ login = view?.formUsernameValue.orEmpty().trim(),
+ password = view?.formPassValue.orEmpty().trim(),
+ baseUrl = view?.formHostValue.orEmpty().trim()
+ )
+ when (it.data.size) {
+ 0 -> view?.navigateToSymbol(loginData)
+ else -> view?.navigateToStudentSelect(it.data)
+ }
}
Status.ERROR -> {
Timber.i("Login result: An exception occurred")
@@ -172,8 +184,12 @@ class LoginAdvancedPresenter @Inject constructor(
return when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) {
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)
+ Sdk.Mode.SCRAPPER -> studentRepository.getStudentsScrapper(
+ email, password, endpoint, symbol
+ )
+ Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid(
+ email, password, endpoint, symbol
+ )
}
}
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 1d2b2856d..f9b84f1ab 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
@@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.login.advanced
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
+import io.github.wulkanowy.ui.modules.login.LoginData
interface LoginAdvancedView : BaseView {
@@ -69,7 +70,9 @@ interface LoginAdvancedView : BaseView {
fun showContent(show: Boolean)
- fun notifyParentAccountLogged(studentsWithSemesters: List)
+ fun navigateToSymbol(loginData: LoginData)
+
+ fun navigateToStudentSelect(studentsWithSemesters: List)
fun setErrorPinRequired()
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 c741da429..d31f5cf0f 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
@@ -13,6 +13,7 @@ import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.FragmentLoginFormBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
+import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.openEmailClient
@@ -68,6 +69,8 @@ class LoginFormFragment : BaseFragment(R.layout.fragme
}
override fun initView() {
+ (requireActivity() as LoginActivity).showActionBar(false)
+
hostKeys = resources.getStringArray(R.array.hosts_keys)
hostValues = resources.getStringArray(R.array.hosts_values)
hostSymbols = resources.getStringArray(R.array.hosts_symbols)
@@ -203,11 +206,9 @@ class LoginFormFragment : BaseFragment(R.layout.fragme
binding.loginFormVersion.text = "v${appInfo.versionName}"
}
- override fun notifyParentAccountLogged(
- studentsWithSemesters: List,
- loginData: Triple
- ) {
- (activity as? LoginActivity)?.onFormFragmentAccountLogged(studentsWithSemesters, loginData)
+ override fun showContact(show: Boolean) {
+ binding.loginFormContact.visibility = if (show) VISIBLE else GONE
+ binding.loginFormRecoverLink.visibility = if (show) GONE else VISIBLE
}
override fun openPrivacyPolicyPage() {
@@ -217,9 +218,12 @@ class LoginFormFragment : BaseFragment(R.layout.fragme
)
}
- override fun showContact(show: Boolean) {
- binding.loginFormContact.visibility = if (show) VISIBLE else GONE
- binding.loginFormRecoverLink.visibility = if (show) GONE else VISIBLE
+ override fun navigateToSymbol(loginData: LoginData) {
+ (activity as? LoginActivity)?.navigateToSymbolFragment(loginData)
+ }
+
+ override fun navigateToStudentSelect(studentsWithSemesters: List) {
+ (activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters)
}
override fun openAdvancedLogin() {
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 1002549cf..49be6fbb6 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
@@ -4,6 +4,7 @@ import androidx.core.net.toUri
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading
@@ -118,7 +119,10 @@ class LoginFormPresenter @Inject constructor(
"scrapperBaseUrl" to host,
"error" to "No error"
)
- view?.notifyParentAccountLogged(it.data, Triple(email, password, host))
+ when (it.data.size) {
+ 0 -> view?.navigateToSymbol(LoginData(email, password, host))
+ else -> view?.navigateToStudentSelect(it.data)
+ }
}
Status.ERROR -> {
Timber.i("Login result: An exception occurred")
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 300573559..8003975db 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
@@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.login.form
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.ui.base.BaseView
+import io.github.wulkanowy.ui.modules.login.LoginData
interface LoginFormView : BaseView {
@@ -57,7 +58,9 @@ interface LoginFormView : BaseView {
fun showVersion()
- fun notifyParentAccountLogged(studentsWithSemesters: List, loginData: Triple)
+ fun navigateToSymbol(loginData: LoginData)
+
+ fun navigateToStudentSelect(studentsWithSemesters: List)
fun openPrivacyPolicyPage()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt
index a91dfb618..fe32a14f1 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt
@@ -69,6 +69,8 @@ class LoginRecoverFragment :
}
override fun initView() {
+ (requireActivity() as LoginActivity).showActionBar(true)
+
hostKeys = resources.getStringArray(R.array.hosts_keys)
hostValues = resources.getStringArray(R.array.hosts_values)
hostSymbols = resources.getStringArray(R.array.hosts_symbols)
@@ -80,7 +82,7 @@ class LoginRecoverFragment :
loginRecoverButton.setOnClickListener { presenter.onRecoverClick() }
loginRecoverErrorRetry.setOnClickListener { presenter.onRecoverClick() }
loginRecoverErrorDetails.setOnClickListener { presenter.onDetailsClick() }
- loginRecoverLogin.setOnClickListener { (activity as LoginActivity).switchView(0) }
+ loginRecoverLogin.setOnClickListener { (activity as LoginActivity).onBackPressed() }
}
with(bindingLocal.loginRecoverHost) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt
index ac4c03130..28686d626 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/RecoverErrorHandler.kt
@@ -8,8 +8,9 @@ import io.github.wulkanowy.sdk.scrapper.exception.NoAccountFoundException
import io.github.wulkanowy.ui.base.ErrorHandler
import javax.inject.Inject
-class RecoverErrorHandler @Inject constructor(@ApplicationContext context: Context) :
- ErrorHandler(context) {
+class RecoverErrorHandler @Inject constructor(
+ @ApplicationContext context: Context,
+) : ErrorHandler(context) {
var onInvalidUsername: (String) -> Unit = {}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
index 87cb505c4..6c910fe03 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
@@ -4,17 +4,18 @@ import android.os.Bundle
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
+import androidx.core.os.bundleOf
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding
import io.github.wulkanowy.ui.base.BaseFragment
+import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.openEmailClient
import io.github.wulkanowy.utils.openInternetBrowser
-import java.io.Serializable
import javax.inject.Inject
@AndroidEntryPoint
@@ -32,18 +33,27 @@ class LoginStudentSelectFragment :
lateinit var appInfo: AppInfo
companion object {
- const val SAVED_STUDENTS = "STUDENTS"
+ const val ARG_STUDENTS = "STUDENTS"
- fun newInstance() = LoginStudentSelectFragment()
+ fun newInstance(studentsWithSemesters: List) =
+ LoginStudentSelectFragment().apply {
+ arguments = bundleOf(ARG_STUDENTS to studentsWithSemesters)
+ }
}
+ @Suppress("UNCHECKED_CAST")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentLoginStudentSelectBinding.bind(view)
- presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_STUDENTS))
+ presenter.onAttachView(
+ view = this,
+ students = requireArguments().getSerializable(ARG_STUDENTS) as List,
+ )
}
override fun initView() {
+ (requireActivity() as LoginActivity).showActionBar(true)
+
loginAdapter.onClickListener = presenter::onItemSelected
with(binding) {
@@ -82,15 +92,6 @@ class LoginStudentSelectFragment :
binding.loginStudentSelectSignIn.isEnabled = enable
}
- fun onParentInitStudentSelectFragment(studentsWithSemesters: List) {
- presenter.onParentInitStudentSelectView(studentsWithSemesters)
- }
-
- override fun onSaveInstanceState(outState: Bundle) {
- super.onSaveInstanceState(outState)
- outState.putSerializable(SAVED_STUDENTS, presenter.students as Serializable)
- }
-
override fun showContact(show: Boolean) {
binding.loginStudentSelectContact.visibility = if (show) VISIBLE else GONE
}
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 f0f5586cc..8c475a672 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
@@ -11,7 +11,6 @@ import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
-import java.io.Serializable
import javax.inject.Inject
class LoginStudentSelectPresenter @Inject constructor(
@@ -22,11 +21,9 @@ class LoginStudentSelectPresenter @Inject constructor(
private var lastError: Throwable? = null
- var students = emptyList()
-
private val selectedStudents = mutableListOf()
- fun onAttachView(view: LoginStudentSelectView, students: Serializable?) {
+ fun onAttachView(view: LoginStudentSelectView, students: List) {
super.onAttachView(view)
with(view) {
initView()
@@ -38,20 +35,14 @@ class LoginStudentSelectPresenter @Inject constructor(
}
}
- if (students is List<*> && students.isNotEmpty()) {
- loadData(students.filterIsInstance())
- }
+ if (students.size == 1) registerStudents(students)
+ loadData(students)
}
fun onSignIn() {
registerStudents(selectedStudents)
}
- fun onParentInitStudentSelectView(studentsWithSemesters: List) {
- loadData(studentsWithSemesters)
- if (studentsWithSemesters.size == 1) registerStudents(studentsWithSemesters)
- }
-
fun onItemSelected(studentWithSemester: StudentWithSemesters, alreadySaved: Boolean) {
if (alreadySaved) return
@@ -72,7 +63,6 @@ class LoginStudentSelectPresenter @Inject constructor(
private fun loadData(studentsWithSemesters: List) {
resetSelectedState()
- this.students = studentsWithSemesters
flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
when (it.status) {
@@ -143,7 +133,8 @@ class LoginStudentSelectPresenter @Inject constructor(
"success" to (error != null),
"scrapperBaseUrl" to student.student.scrapperBaseUrl,
"symbol" to student.student.symbol,
- "error" to (error?.message?.ifBlank { "No message" } ?: "No error"))
+ "error" to (error?.message?.ifBlank { "No message" } ?: "No error")
+ )
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt
index a8086935b..58bdf6cef 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt
@@ -7,6 +7,7 @@ import android.view.View.VISIBLE
import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
import android.view.inputmethod.EditorInfo.IME_NULL
import android.widget.ArrayAdapter
+import androidx.core.os.bundleOf
import androidx.core.text.parseAsHtml
import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
@@ -15,6 +16,7 @@ import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.databinding.FragmentLoginSymbolBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
+import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.openEmailClient
@@ -35,7 +37,9 @@ class LoginSymbolFragment :
companion object {
private const val SAVED_LOGIN_DATA = "LOGIN_DATA"
- fun newInstance() = LoginSymbolFragment()
+ fun newInstance(loginData: LoginData) = LoginSymbolFragment().apply {
+ arguments = bundleOf(SAVED_LOGIN_DATA to loginData)
+ }
}
override val symbolNameError: CharSequence?
@@ -44,10 +48,15 @@ class LoginSymbolFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentLoginSymbolBinding.bind(view)
- presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_LOGIN_DATA))
+ presenter.onAttachView(
+ view = this,
+ loginData = requireArguments().getSerializable(SAVED_LOGIN_DATA) as LoginData,
+ )
}
override fun initView() {
+ (requireActivity() as LoginActivity).showActionBar(true)
+
with(binding) {
loginSymbolSignIn.setOnClickListener { presenter.attemptLogin(loginSymbolName.text.toString()) }
loginSymbolFaq.setOnClickListener { presenter.onFaqClick() }
@@ -70,12 +79,9 @@ class LoginSymbolFragment :
}
}
- fun onParentInitSymbolFragment(loginData: Triple) {
- presenter.onParentInitSymbolView(loginData)
- }
-
override fun setLoginToHeading(login: String) {
- binding.loginSymbolHeader.text = getString(R.string.login_header_symbol, login).parseAsHtml()
+ binding.loginSymbolHeader.text =
+ getString(R.string.login_header_symbol, login).parseAsHtml()
}
override fun setErrorSymbolIncorrect() {
@@ -119,8 +125,8 @@ class LoginSymbolFragment :
binding.loginSymbolContainer.visibility = if (show) VISIBLE else GONE
}
- override fun notifyParentAccountLogged(studentsWithSemesters: List) {
- (activity as? LoginActivity)?.onSymbolFragmentAccountLogged(studentsWithSemesters)
+ override fun navigateToStudentSelect(studentsWithSemesters: List) {
+ (activity as? LoginActivity)?.navigateToStudentSelect(studentsWithSemesters)
}
override fun onSaveInstanceState(outState: Bundle) {
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 1ba7e5b32..7e195893f 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
@@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.login.symbol
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading
@@ -10,7 +11,6 @@ import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
-import java.io.Serializable
import javax.inject.Inject
class LoginSymbolPresenter @Inject constructor(
@@ -21,25 +21,15 @@ class LoginSymbolPresenter @Inject constructor(
private var lastError: Throwable? = null
- var loginData: Triple? = null
+ lateinit var loginData: LoginData
- @Suppress("UNCHECKED_CAST")
- fun onAttachView(view: LoginSymbolView, savedLoginData: Serializable?) {
+ fun onAttachView(view: LoginSymbolView, loginData: LoginData) {
super.onAttachView(view)
- view.run {
+ this.loginData = loginData
+ with(view) {
initView()
showContact(false)
- }
- if (savedLoginData is Triple<*, *, *>) {
- loginData = savedLoginData as Triple
- view.setLoginToHeading(requireNotNull(loginData?.first))
- }
- }
-
- fun onParentInitSymbolView(loginData: Triple) {
- this.loginData = loginData
- view?.apply {
- setLoginToHeading(loginData.first)
+ setLoginToHeading(loginData.login)
clearAndFocusSymbol()
showSoftKeyboard()
}
@@ -50,11 +40,6 @@ class LoginSymbolPresenter @Inject constructor(
}
fun attemptLogin(symbol: String) {
- if (loginData == null) {
- Timber.w("LoginSymbolPresenter - Login data is null")
- return
- }
-
if (symbol.isBlank()) {
view?.setErrorSymbolRequire()
return
@@ -62,9 +47,9 @@ class LoginSymbolPresenter @Inject constructor(
flowWithResource {
studentRepository.getStudentsScrapper(
- email = loginData!!.first,
- password = loginData!!.second,
- scrapperBaseUrl = loginData!!.third,
+ email = loginData.login,
+ password = loginData.password,
+ scrapperBaseUrl = loginData.baseUrl,
symbol = symbol,
)
}.onEach {
@@ -76,21 +61,24 @@ class LoginSymbolPresenter @Inject constructor(
showContent(false)
}
Status.SUCCESS -> {
- view?.run {
- if (it.data!!.isEmpty()) {
+ when (it.data?.size) {
+ 0 -> {
Timber.i("Login with symbol result: Empty student list")
- setErrorSymbolIncorrect()
- view?.showContact(true)
- } else {
+ view?.run {
+ setErrorSymbolIncorrect()
+ showContact(true)
+ }
+ }
+ else -> {
Timber.i("Login with symbol result: Success")
- notifyParentAccountLogged(it.data)
+ view?.navigateToStudentSelect(requireNotNull(it.data))
}
}
analytics.logEvent(
"registration_symbol",
"success" to true,
"students" to it.data!!.size,
- "scrapperBaseUrl" to loginData?.third,
+ "scrapperBaseUrl" to loginData.baseUrl,
"symbol" to symbol,
"error" to "No error"
)
@@ -101,7 +89,7 @@ class LoginSymbolPresenter @Inject constructor(
"registration_symbol",
"success" to false,
"students" to -1,
- "scrapperBaseUrl" to loginData?.third,
+ "scrapperBaseUrl" to loginData.baseUrl,
"symbol" to symbol,
"error" to it.error!!.message.ifNullOrBlank { "No message" }
)
@@ -123,6 +111,6 @@ class LoginSymbolPresenter @Inject constructor(
}
fun onEmailClick() {
- view?.openEmail(loginData?.third.orEmpty(), lastError?.message.ifNullOrBlank { "empty" })
+ view?.openEmail(loginData.baseUrl, lastError?.message.ifNullOrBlank { "empty" })
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt
index 75523a7c7..527895b77 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolView.kt
@@ -27,7 +27,7 @@ interface LoginSymbolView : BaseView {
fun showContent(show: Boolean)
- fun notifyParentAccountLogged(studentsWithSemesters: List)
+ fun navigateToStudentSelect(studentsWithSemesters: List)
fun showContact(show: Boolean)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt
index a2265b044..bef726cad 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt
@@ -4,7 +4,6 @@ import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import androidx.preference.PreferenceFragmentCompat
-import com.yariksoffice.lingver.Lingver
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseActivity
@@ -24,9 +23,6 @@ class AdvancedFragment : PreferenceFragmentCompat(),
@Inject
lateinit var appInfo: AppInfo
- @Inject
- lateinit var lingver: Lingver
-
override val titleStringId get() = R.string.pref_settings_advanced_title
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index 1d5b5280d..912792638 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -1,5 +1,5 @@
@@ -10,8 +10,11 @@
android:layout_height="wrap_content"
android:background="@android:color/transparent" />
-
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ tools:layout="@layout/fragment_login_form" />
+
diff --git a/app/src/main/res/layout/fragment_login_recover.xml b/app/src/main/res/layout/fragment_login_recover.xml
index 76bdad228..028d6bc1f 100644
--- a/app/src/main/res/layout/fragment_login_recover.xml
+++ b/app/src/main/res/layout/fragment_login_recover.xml
@@ -155,7 +155,7 @@
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
- tools:visibility="visible">
+ tools:visibility="gone">
Date: Mon, 27 Dec 2021 08:10:30 +0100
Subject: [PATCH 011/669] Fix that an incorrect day would be selected in
MaterialDatePicker (#1723)
---
.../modules/attendance/AttendanceFragment.kt | 38 ++++----------
.../ui/modules/attendance/AttendanceView.kt | 2 +-
.../modules/homework/add/HomeworkAddDialog.kt | 38 +++++---------
.../modules/homework/add/HomeworkAddView.kt | 2 +-
.../history/LuckyNumberHistoryFragment.kt | 38 ++++----------
.../history/LuckyNumberHistoryView.kt | 2 +-
.../ui/modules/timetable/TimetableFragment.kt | 38 ++++----------
.../ui/modules/timetable/TimetableView.kt | 2 +-
.../additional/AdditionalLessonsFragment.kt | 36 ++++---------
.../additional/AdditionalLessonsView.kt | 2 +-
.../add/AdditionalLessonAddDialog.kt | 36 ++++---------
.../completed/CompletedLessonsFragment.kt | 36 ++++---------
.../completed/CompletedLessonsView.kt | 2 +-
.../utils/MaterialDatePickerUtils.kt | 51 +++++++++++++++++++
.../wulkanowy/utils/SchooldaysValidator.kt | 16 ------
.../github/wulkanowy/utils/TimeExtension.kt | 11 ++--
.../wulkanowy/utils/TimeExtensionTest.kt | 20 +++++++-
17 files changed, 153 insertions(+), 217 deletions(-)
create mode 100644 app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt
delete mode 100644 app/src/main/java/io/github/wulkanowy/utils/SchooldaysValidator.kt
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 9b5a3fa36..84af1ca32 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
@@ -14,8 +14,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
-import com.google.android.material.datepicker.CalendarConstraints
-import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Attendance
@@ -27,12 +25,10 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.toLocalDateTime
-import io.github.wulkanowy.utils.toTimestamp
+import io.github.wulkanowy.utils.openMaterialDatePicker
import java.time.LocalDate
import javax.inject.Inject
@@ -224,29 +220,15 @@ class AttendanceFragment : BaseFragment(R.layout.frag
(activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson))
}
- override fun showDatePickerDialog(currentDate: LocalDate) {
- val baseDate = currentDate.firstSchoolDayInSchoolYear
- val rangeStart = baseDate.toTimestamp()
- val rangeEnd = LocalDate.now().plusWeeks(1).toTimestamp()
-
- val constraintsBuilder = CalendarConstraints.Builder().apply {
- setValidator(SchoolDaysValidator(rangeStart, rangeEnd))
- setStart(rangeStart)
- setEnd(rangeEnd)
- }
- val datePicker = MaterialDatePicker.Builder.datePicker()
- .setCalendarConstraints(constraintsBuilder.build())
- .setSelection(currentDate.toTimestamp())
- .build()
-
- datePicker.addOnPositiveButtonClickListener {
- val date = it.toLocalDateTime()
- presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth)
- }
-
- if (!parentFragmentManager.isStateSaved) {
- datePicker.show(parentFragmentManager, null)
- }
+ override fun showDatePickerDialog(selectedDate: LocalDate) {
+ openMaterialDatePicker(
+ selected = selectedDate,
+ rangeStart = selectedDate.firstSchoolDayInSchoolYear,
+ rangeEnd = LocalDate.now().plusWeeks(1),
+ onDateSelected = {
+ presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth)
+ }
+ )
}
override fun showExcuseDialog() {
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 7ddd75f48..b0123065a 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
@@ -48,7 +48,7 @@ interface AttendanceView : BaseView {
fun showAttendanceDialog(lesson: Attendance)
- fun showDatePickerDialog(currentDate: LocalDate)
+ fun showDatePickerDialog(selectedDate: LocalDate)
fun showExcuseDialog()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt
index 0f285b13d..c2aff2b13 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddDialog.kt
@@ -5,17 +5,13 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.widget.doOnTextChanged
-import com.google.android.material.datepicker.CalendarConstraints
-import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.DialogHomeworkAddBinding
import io.github.wulkanowy.ui.base.BaseDialogFragment
-import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
+import io.github.wulkanowy.utils.openMaterialDatePicker
import io.github.wulkanowy.utils.toFormattedString
-import io.github.wulkanowy.utils.toLocalDateTime
-import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
import javax.inject.Inject
@@ -25,6 +21,7 @@ class HomeworkAddDialog : BaseDialogFragment(), Homewo
@Inject
lateinit var presenter: HomeworkAddPresenter
+ // todo: move it to presenter
private var date: LocalDate? = null
override fun onCreate(savedInstanceState: Bundle?) {
@@ -99,27 +96,16 @@ class HomeworkAddDialog : BaseDialogFragment(), Homewo
dismiss()
}
- override fun showDatePickerDialog(currentDate: LocalDate) {
- val rangeStart = LocalDate.now().toTimestamp()
- val rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear.toTimestamp()
- val constraintsBuilder = CalendarConstraints.Builder().apply {
- setStart(rangeStart)
- setEnd(rangeEnd)
- setValidator(SchoolDaysValidator(rangeStart, rangeEnd))
- }
- val datePicker = MaterialDatePicker.Builder.datePicker()
- .setCalendarConstraints(constraintsBuilder.build())
- .setSelection(currentDate.toTimestamp())
- .build()
-
- datePicker.addOnPositiveButtonClickListener {
- date = it.toLocalDateTime().toLocalDate()
- binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString())
- }
-
- if (!parentFragmentManager.isStateSaved) {
- datePicker.show(this.parentFragmentManager, null)
- }
+ override fun showDatePickerDialog(selectedDate: LocalDate) {
+ openMaterialDatePicker(
+ selected = selectedDate,
+ rangeStart = LocalDate.now(),
+ rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear,
+ onDateSelected = {
+ date = it
+ binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString())
+ }
+ )
}
override fun onDestroyView() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddView.kt
index 3bb304d9c..91414ae2f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddView.kt
@@ -17,5 +17,5 @@ interface HomeworkAddView : BaseView {
fun closeDialog()
- fun showDatePickerDialog(currentDate: LocalDate)
+ fun showDatePickerDialog(selectedDate: LocalDate)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt
index 3bbed18b7..53f06cacd 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryFragment.kt
@@ -5,8 +5,6 @@ import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.recyclerview.widget.LinearLayoutManager
-import com.google.android.material.datepicker.CalendarConstraints
-import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.LuckyNumber
@@ -14,11 +12,9 @@ import io.github.wulkanowy.databinding.FragmentLuckyNumberHistoryBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
-import io.github.wulkanowy.utils.toLocalDateTime
-import io.github.wulkanowy.utils.toTimestamp
+import io.github.wulkanowy.utils.openMaterialDatePicker
import java.time.LocalDate
import javax.inject.Inject
@@ -111,29 +107,15 @@ class LuckyNumberHistoryFragment :
binding.luckyNumberHistoryNextButton.visibility = if (show) VISIBLE else View.INVISIBLE
}
- override fun showDatePickerDialog(currentDate: LocalDate) {
- val baseDate = currentDate.firstSchoolDayInSchoolYear
- val rangeStart = baseDate.toTimestamp()
- val rangeEnd = LocalDate.now().plusWeeks(1).toTimestamp()
-
- val constraintsBuilder = CalendarConstraints.Builder().apply {
- setValidator(SchoolDaysValidator(rangeStart, rangeEnd))
- setStart(rangeStart)
- setEnd(rangeEnd)
- }
- val datePicker = MaterialDatePicker.Builder.datePicker()
- .setCalendarConstraints(constraintsBuilder.build())
- .setSelection(currentDate.toTimestamp())
- .build()
-
- datePicker.addOnPositiveButtonClickListener {
- val date = it.toLocalDateTime()
- presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth)
- }
-
- if (!parentFragmentManager.isStateSaved) {
- datePicker.show(parentFragmentManager, null)
- }
+ override fun showDatePickerDialog(selectedDate: LocalDate) {
+ openMaterialDatePicker(
+ selected = selectedDate,
+ rangeStart = selectedDate.firstSchoolDayInSchoolYear,
+ rangeEnd = LocalDate.now().plusWeeks(1),
+ onDateSelected = {
+ presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth)
+ }
+ )
}
override fun showContent(show: Boolean) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt
index 331e4ff86..7b9b0294f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryView.kt
@@ -28,7 +28,7 @@ interface LuckyNumberHistoryView : BaseView {
fun showNextButton(show: Boolean)
- fun showDatePickerDialog(currentDate: LocalDate)
+ fun showDatePickerDialog(selectedDate: LocalDate)
fun showContent(show: Boolean)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
index dd1136398..f59c6432c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
@@ -9,8 +9,6 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.core.text.parseAsHtml
import androidx.recyclerview.widget.LinearLayoutManager
-import com.google.android.material.datepicker.CalendarConstraints
-import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Timetable
@@ -22,13 +20,11 @@ import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.timetable.additional.AdditionalLessonsFragment
import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
-import io.github.wulkanowy.utils.toLocalDateTime
-import io.github.wulkanowy.utils.toTimestamp
+import io.github.wulkanowy.utils.openMaterialDatePicker
import java.time.LocalDate
import javax.inject.Inject
@@ -193,29 +189,15 @@ class TimetableFragment : BaseFragment(R.layout.fragme
(activity as? MainActivity)?.showDialogFragment(TimetableDialog.newInstance(lesson))
}
- override fun showDatePickerDialog(currentDate: LocalDate) {
- val baseDate = currentDate.firstSchoolDayInSchoolYear
- val rangeStart = baseDate.toTimestamp()
- val rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear.toTimestamp()
-
- val constraintsBuilder = CalendarConstraints.Builder().apply {
- setValidator(SchoolDaysValidator(rangeStart, rangeEnd))
- setStart(rangeStart)
- setEnd(rangeEnd)
- }
- val datePicker = MaterialDatePicker.Builder.datePicker()
- .setCalendarConstraints(constraintsBuilder.build())
- .setSelection(currentDate.toTimestamp())
- .build()
-
- datePicker.addOnPositiveButtonClickListener {
- val date = it.toLocalDateTime()
- presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth)
- }
-
- if (!parentFragmentManager.isStateSaved) {
- datePicker.show(parentFragmentManager, null)
- }
+ override fun showDatePickerDialog(selectedDate: LocalDate) {
+ openMaterialDatePicker(
+ selected = selectedDate,
+ rangeStart = selectedDate.firstSchoolDayInSchoolYear,
+ rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear,
+ onDateSelected = {
+ presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth)
+ }
+ )
}
override fun openAdditionalLessonsView() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt
index eaf75cb0e..4f6af4b9e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt
@@ -48,7 +48,7 @@ interface TimetableView : BaseView {
fun showTimetableDialog(lesson: Timetable)
- fun showDatePickerDialog(currentDate: LocalDate)
+ fun showDatePickerDialog(selectedDate: LocalDate)
fun popView()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt
index b2a4a9a32..043fa1f7d 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsFragment.kt
@@ -4,8 +4,6 @@ import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
-import com.google.android.material.datepicker.CalendarConstraints
-import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.TimetableAdditional
@@ -15,13 +13,11 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.timetable.additional.add.AdditionalLessonAddDialog
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
-import io.github.wulkanowy.utils.toLocalDateTime
-import io.github.wulkanowy.utils.toTimestamp
+import io.github.wulkanowy.utils.openMaterialDatePicker
import java.time.LocalDate
import javax.inject.Inject
@@ -144,29 +140,17 @@ class AdditionalLessonsFragment :
(activity as? MainActivity)?.showDialogFragment(AdditionalLessonAddDialog.newInstance())
}
- override fun showDatePickerDialog(currentDate: LocalDate) {
+ override fun showDatePickerDialog(selectedDate: LocalDate) {
val now = LocalDate.now()
- val startOfSchoolYear = now.firstSchoolDayInSchoolYear.toTimestamp()
- val endOfSchoolYear = now.lastSchoolDayInSchoolYear.toTimestamp()
- val constraintsBuilder = CalendarConstraints.Builder().apply {
- setValidator(SchoolDaysValidator(startOfSchoolYear, endOfSchoolYear))
- setStart(startOfSchoolYear)
- setEnd(endOfSchoolYear)
- }
- val datePicker = MaterialDatePicker.Builder.datePicker()
- .setCalendarConstraints(constraintsBuilder.build())
- .setSelection(currentDate.toTimestamp())
- .build()
-
- datePicker.addOnPositiveButtonClickListener {
- val date = it.toLocalDateTime()
- presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth)
- }
-
- if (!parentFragmentManager.isStateSaved) {
- datePicker.show(parentFragmentManager, null)
- }
+ openMaterialDatePicker(
+ selected = selectedDate,
+ rangeStart = now.firstSchoolDayInSchoolYear,
+ rangeEnd = now.lastSchoolDayInSchoolYear,
+ onDateSelected = {
+ presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth)
+ }
+ )
}
override fun showDeleteLessonDialog(timetableAdditional: TimetableAdditional) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt
index 03466d69d..76d37b754 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsView.kt
@@ -34,7 +34,7 @@ interface AdditionalLessonsView : BaseView {
fun showNextButton(show: Boolean)
- fun showDatePickerDialog(currentDate: LocalDate)
+ fun showDatePickerDialog(selectedDate: LocalDate)
fun showAddAdditionalLessonDialog()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt
index f57841c9c..f82d64830 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddDialog.kt
@@ -5,19 +5,15 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.widget.doOnTextChanged
-import com.google.android.material.datepicker.CalendarConstraints
-import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.DialogAdditionalAddBinding
import io.github.wulkanowy.ui.base.BaseDialogFragment
-import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
+import io.github.wulkanowy.utils.openMaterialDatePicker
import io.github.wulkanowy.utils.toFormattedString
-import io.github.wulkanowy.utils.toLocalDateTime
-import io.github.wulkanowy.utils.toTimestamp
import java.time.LocalDate
import java.time.LocalTime
import javax.inject.Inject
@@ -128,27 +124,15 @@ class AdditionalLessonAddDialog : BaseDialogFragment
}
override fun showDatePickerDialog(selectedDate: LocalDate) {
- val rangeStart = LocalDate.now().toTimestamp()
- val rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear.toTimestamp()
- val constraintsBuilder = CalendarConstraints.Builder().apply {
- setStart(rangeStart)
- setEnd(rangeEnd)
- setValidator(SchoolDaysValidator(rangeStart, rangeEnd))
- }
- val datePicker = MaterialDatePicker.Builder.datePicker()
- .setCalendarConstraints(constraintsBuilder.build())
- .setSelection(selectedDate.toTimestamp())
- .build()
-
- datePicker.addOnPositiveButtonClickListener {
- val date = it.toLocalDateTime().toLocalDate()
- presenter.onDateSelected(date)
- binding.additionalLessonDialogDateEdit.setText(date.toFormattedString())
- }
-
- if (!parentFragmentManager.isStateSaved) {
- datePicker.show(parentFragmentManager, null)
- }
+ openMaterialDatePicker(
+ selected = selectedDate,
+ rangeStart = LocalDate.now(),
+ rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear,
+ onDateSelected = {
+ presenter.onDateSelected(it)
+ binding.additionalLessonDialogDateEdit.setText(it.toFormattedString())
+ }
+ )
}
override fun showStartTimePickerDialog(selectedTime: LocalTime) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt
index 4d5e3e1a5..34a69e6ab 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt
@@ -6,8 +6,6 @@ import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import androidx.recyclerview.widget.LinearLayoutManager
-import com.google.android.material.datepicker.CalendarConstraints
-import com.google.android.material.datepicker.MaterialDatePicker
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.CompletedLesson
@@ -16,14 +14,12 @@ import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.SchoolDaysValidator
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
import io.github.wulkanowy.utils.getCompatDrawable
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
-import io.github.wulkanowy.utils.toLocalDateTime
-import io.github.wulkanowy.utils.toTimestamp
+import io.github.wulkanowy.utils.openMaterialDatePicker
import java.time.LocalDate
import javax.inject.Inject
@@ -150,29 +146,17 @@ class CompletedLessonsFragment :
)
}
- override fun showDatePickerDialog(currentDate: LocalDate) {
+ override fun showDatePickerDialog(selectedDate: LocalDate) {
val now = LocalDate.now()
- val startOfSchoolYear = now.firstSchoolDayInSchoolYear.toTimestamp()
- val endOfSchoolYear = now.lastSchoolDayInSchoolYear.toTimestamp()
- val constraintsBuilder = CalendarConstraints.Builder().apply {
- setValidator(SchoolDaysValidator(startOfSchoolYear, endOfSchoolYear))
- setStart(startOfSchoolYear)
- setEnd(endOfSchoolYear)
- }
- val datePicker = MaterialDatePicker.Builder.datePicker()
- .setCalendarConstraints(constraintsBuilder.build())
- .setSelection(currentDate.toTimestamp())
- .build()
-
- datePicker.addOnPositiveButtonClickListener {
- val date = it.toLocalDateTime()
- presenter.onDateSet(date.year, date.monthValue, date.dayOfMonth)
- }
-
- if (!parentFragmentManager.isStateSaved) {
- datePicker.show(parentFragmentManager, null)
- }
+ openMaterialDatePicker(
+ selected = selectedDate,
+ rangeStart = now.firstSchoolDayInSchoolYear,
+ rangeEnd = now.lastSchoolDayInSchoolYear,
+ onDateSelected = {
+ presenter.onDateSet(it.year, it.monthValue, it.dayOfMonth)
+ }
+ )
}
override fun onSaveInstanceState(outState: Bundle) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt
index 7a98874e0..715ce01fc 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt
@@ -38,5 +38,5 @@ interface CompletedLessonsView : BaseView {
fun showCompletedLessonDialog(completedLesson: CompletedLesson)
- fun showDatePickerDialog(currentDate: LocalDate)
+ fun showDatePickerDialog(selectedDate: LocalDate)
}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt
new file mode 100644
index 000000000..14b5989b0
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt
@@ -0,0 +1,51 @@
+package io.github.wulkanowy.utils
+
+import androidx.fragment.app.Fragment
+import com.google.android.material.datepicker.CalendarConstraints
+import com.google.android.material.datepicker.MaterialDatePicker
+import kotlinx.parcelize.Parcelize
+import java.time.LocalDate
+import java.time.ZoneOffset
+import java.time.temporal.ChronoUnit
+
+fun Fragment.openMaterialDatePicker(
+ selected: LocalDate,
+ rangeStart: LocalDate,
+ rangeEnd: LocalDate,
+ onDateSelected: (LocalDate) -> Unit,
+) {
+ val constraintsBuilder = CalendarConstraints.Builder().apply {
+ setValidator(CalendarDayRangeValidator(rangeStart, rangeEnd))
+ setStart(rangeStart.toTimestamp(ZoneOffset.UTC))
+ setEnd(rangeEnd.toTimestamp(ZoneOffset.UTC))
+ }
+
+ val datePicker = MaterialDatePicker.Builder.datePicker()
+ .setCalendarConstraints(constraintsBuilder.build())
+ .setSelection(selected.toTimestamp(ZoneOffset.UTC))
+ .build()
+
+ datePicker.addOnPositiveButtonClickListener {
+ val date = it.toLocalDateTime(ZoneOffset.UTC).toLocalDate()
+ onDateSelected(date)
+ }
+
+ if (!parentFragmentManager.isStateSaved) {
+ datePicker.show(parentFragmentManager, null)
+ }
+}
+
+@Parcelize
+private class CalendarDayRangeValidator(
+ val start: LocalDate,
+ val end: LocalDate,
+) : CalendarConstraints.DateValidator {
+
+ override fun isValid(dateLong: Long): Boolean {
+ val date = dateLong.toLocalDateTime().toLocalDate()
+ val daysUntilEnd = date.until(end, ChronoUnit.DAYS)
+ val daysUntilStart = date.until(start, ChronoUnit.DAYS)
+
+ return daysUntilStart <= 0 && daysUntilEnd >= 0
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/SchooldaysValidator.kt b/app/src/main/java/io/github/wulkanowy/utils/SchooldaysValidator.kt
deleted file mode 100644
index b6dd528f5..000000000
--- a/app/src/main/java/io/github/wulkanowy/utils/SchooldaysValidator.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.github.wulkanowy.utils
-
-import com.google.android.material.datepicker.CalendarConstraints
-import kotlinx.parcelize.Parcelize
-import java.time.temporal.ChronoUnit
-
-@Parcelize
-class SchoolDaysValidator(val start: Long, val end: Long) : CalendarConstraints.DateValidator {
-
- override fun isValid(dateLong: Long): Boolean {
- val date = dateLong.toLocalDateTime()
-
- return date.until(end.toLocalDateTime(), ChronoUnit.DAYS) >= 0 &&
- date.until(start.toLocalDateTime(), ChronoUnit.DAYS) <= 0
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt
index bebb6c541..355f3ab49 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt
@@ -8,7 +8,6 @@ import java.time.DayOfWeek.SUNDAY
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
-import java.time.LocalTime
import java.time.Month
import java.time.ZoneId
import java.time.ZoneOffset
@@ -23,13 +22,13 @@ private const val DEFAULT_DATE_PATTERN = "dd.MM.yyyy"
fun String.toLocalDate(format: String = DEFAULT_DATE_PATTERN): LocalDate =
LocalDate.parse(this, DateTimeFormatter.ofPattern(format))
-fun LocalDateTime.toTimestamp() =
- atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneOffset.UTC).toInstant().toEpochMilli()
+fun LocalDateTime.toTimestamp(tz: ZoneId = ZoneId.systemDefault()) =
+ atZone(tz).withZoneSameInstant(ZoneOffset.UTC).toInstant().toEpochMilli()
-fun Long.toLocalDateTime(): LocalDateTime =
- LocalDateTime.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault())
+fun Long.toLocalDateTime(tz: ZoneId = ZoneId.systemDefault()): LocalDateTime =
+ LocalDateTime.ofInstant(Instant.ofEpochMilli(this), tz)
-fun LocalDate.toTimestamp() = atTime(LocalTime.now()).toTimestamp()
+fun LocalDate.toTimestamp(tz: ZoneId = ZoneId.systemDefault()) = atStartOfDay().toTimestamp(tz)
fun LocalDate.toFormattedString(pattern: String = DEFAULT_DATE_PATTERN): String =
format(DateTimeFormatter.ofPattern(pattern))
diff --git a/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt b/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt
index 0ffbf78ec..9a3bf9fea 100644
--- a/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt
@@ -7,6 +7,7 @@ import org.junit.Test
import java.time.LocalDate.of
import java.time.LocalDateTime
import java.time.Month.JANUARY
+import java.time.ZoneOffset
import java.util.Locale
class TimeExtensionTest {
@@ -25,7 +26,10 @@ class TimeExtensionTest {
@Test
fun toFormattedStringLocalDateTimeTest() {
assertEquals("01.10.2018", LocalDateTime.of(2018, 10, 1, 10, 0, 0).toFormattedString())
- assertEquals("2018-10-01 10:00:00", LocalDateTime.of(2018, 10, 1, 10, 0, 0).toFormattedString("uuuu-MM-dd HH:mm:ss"))
+ assertEquals(
+ "2018-10-01 10:00:00",
+ LocalDateTime.of(2018, 10, 1, 10, 0, 0).toFormattedString("uuuu-MM-dd HH:mm:ss")
+ )
}
@Test
@@ -222,4 +226,18 @@ class TimeExtensionTest {
assertEquals(of(2020, 10, 18), endExamsDay)
}
}
+
+ @Test
+ fun getLocalDateToTimestampUTC() {
+ assertEquals(0L, of(1970, 1, 1).toTimestamp(ZoneOffset.UTC))
+ assertEquals(946684800000L, of(2000, 1, 1).toTimestamp(ZoneOffset.UTC))
+ assertEquals(1640131200000L, of(2021, 12, 22).toTimestamp(ZoneOffset.UTC))
+ }
+
+ @Test
+ fun getLocalDateTimeToUtcTimestamp() {
+ assertEquals(0L, LocalDateTime.of(1970, 1, 1, 0, 0, 0).toTimestamp(ZoneOffset.UTC))
+ assertEquals(946684800000L, LocalDateTime.of(2000, 1, 1, 0, 0, 0).toTimestamp(ZoneOffset.UTC))
+ assertEquals(1640131200000L, LocalDateTime.of(2021, 12, 22, 0, 0, 0).toTimestamp(ZoneOffset.UTC))
+ }
}
From bd883c9f382ec61f16a3ad503deaa66cd7cf9cc5 Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Mon, 27 Dec 2021 08:48:47 +0100
Subject: [PATCH 012/669] Add option to remove notifications captured from
vulcan.hebe (#1716)
---
.../data/repositories/PreferencesRepository.kt | 6 ++++++
.../VulcanNotificationListenerService.kt | 3 +++
app/src/main/res/values/preferences_defaults.xml | 1 +
app/src/main/res/values/preferences_keys.xml | 1 +
app/src/main/res/values/strings.xml | 2 ++
.../res/xml/scheme_preferences_notifications.xml | 15 +++++++++++++--
6 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
index 48eac48a2..e6437b167 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
@@ -133,6 +133,12 @@ class PreferencesRepository @Inject constructor(
R.bool.pref_default_notification_piggyback
)
+ val isNotificationPiggybackRemoveOriginalEnabled: Boolean
+ get() = getBoolean(
+ R.string.pref_key_notifications_piggyback_cancel_original,
+ R.bool.pref_default_notification_piggyback_cancel_original
+ )
+
val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug)
val isDebugNotificationEnable: Boolean
get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
diff --git a/app/src/main/java/io/github/wulkanowy/services/piggyback/VulcanNotificationListenerService.kt b/app/src/main/java/io/github/wulkanowy/services/piggyback/VulcanNotificationListenerService.kt
index c7df2dbc1..3c173495a 100644
--- a/app/src/main/java/io/github/wulkanowy/services/piggyback/VulcanNotificationListenerService.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/piggyback/VulcanNotificationListenerService.kt
@@ -19,6 +19,9 @@ class VulcanNotificationListenerService : NotificationListenerService() {
override fun onNotificationPosted(statusBarNotification: StatusBarNotification?) {
if (statusBarNotification?.packageName == "pl.edu.vulcan.hebe" && preferenceRepository.isNotificationPiggybackEnabled) {
syncManager.startOneTimeSyncWorker()
+ if (preferenceRepository.isNotificationPiggybackRemoveOriginalEnabled) {
+ cancelNotification(statusBarNotification.key)
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/res/values/preferences_defaults.xml b/app/src/main/res/values/preferences_defaults.xml
index 7fb3d5c05..deeb36961 100644
--- a/app/src/main/res/values/preferences_defaults.xml
+++ b/app/src/main/res/values/preferences_defaults.xml
@@ -28,6 +28,7 @@
false
0
false
+ false
- LUCKY_NUMBER
- MESSAGES
diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml
index fef062dd1..849d989ee 100644
--- a/app/src/main/res/values/preferences_keys.xml
+++ b/app/src/main/res/values/preferences_keys.xml
@@ -35,5 +35,6 @@
message_send_recipients
last_sync_date
notifications_piggyback
+ notifications_piggyback_cancel_original
single_ad_support
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index dff5babfd..bdf2935bd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -672,7 +672,9 @@
Your device may have data synchronization issues and with notifications.\n\nTo fix them, you need to add Wulkanowy to the autostart and turn off battery optimization/saving in the phone settings.
Show debug notifications
Synchronization is disabled
+ Official app notifications
Capture official app notifications
+ Remove official app notifications after capture
Capture notifications
With this feature you can gain a substitute of push notifications like in the official app. All you need to do is allow Wulkanowy to receive all notifications in your system settings.\n\nHow it works?\nWhen you get a notification in Dziennik VULCAN, Wulkanowy will be notified (that\'s what these extra permissions are for) and will trigger a sync so that can send its own notification.\n\nFOR ADVANCED USERS ONLY
Upcoming lesson notifications
diff --git a/app/src/main/res/xml/scheme_preferences_notifications.xml b/app/src/main/res/xml/scheme_preferences_notifications.xml
index 442581bfd..0366914ac 100644
--- a/app/src/main/res/xml/scheme_preferences_notifications.xml
+++ b/app/src/main/res/xml/scheme_preferences_notifications.xml
@@ -16,9 +16,9 @@
app:title="@string/pref_notify_upcoming_lessons_switch" />
@@ -31,13 +31,24 @@
+ app:title="@string/pref_notify_notifications_piggyback_header">
+
+
+
Date: Mon, 27 Dec 2021 14:06:20 +0100
Subject: [PATCH 013/669] New Crowdin updates (#1729)
---
app/src/main/res/values-cs/strings.xml | 13 +++++++++++++
app/src/main/res/values-de/strings.xml | 13 +++++++++++++
app/src/main/res/values-pl/strings.xml | 13 +++++++++++++
app/src/main/res/values-ru/strings.xml | 13 +++++++++++++
app/src/main/res/values-sk/strings.xml | 13 +++++++++++++
app/src/main/res/values-uk/strings.xml | 13 +++++++++++++
6 files changed, 78 insertions(+)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 8e49d4f1c..330b75784 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -204,6 +204,17 @@
Další lekce
Zobrazit další lekce
Žádné informace o dalších lekcích
+ Nová lekce
+ Nová další lekce
+ Další lekce byla úspěšně přidána
+ Další lekce byla úspěšně odstraněna
+ Opakovat každý týden
+ Odstranit další lekci
+ Pouze tato lekce
+ Všechny v sérii
+ Čas zahájení
+ Čas ukončení
+ Čas ukončení musí být pozdější než čas zahájení
Shrnutí frekvencí
Neprítomnosť zo školských dôvodov
@@ -668,7 +679,9 @@
Vaše zařízení může mít problémy se synchronizací dat as upozorněními.\n\nChcete-li je opravit, přidejte Wulkanového do funkce Autostart a vypněte optimalizaci/úsporu baterie v nastavení systému telefonu.
Zobrazit upozornění o ladění
Synchronizace je vypnutá
+ Official app notifications
Zachytit upozornění oficiální aplikací
+ Remove official app notifications after capture
Zachytit upozornění
S touto funkcí můžete získat náhradu push upozornění jako v oficiální aplikaci. Vše, co musíte udělat, je povolit Wulkanowému číst všechna vaše upozornění v nastaveních systému.\n\nJak to funguje?\nKdyž obdržíte oznámení v Deníčku VULCAN, Wulkanowy bude o tom informován (k tomu je to dodatečné povolení) a spustí synchronizaci, aby mohl zaslat vlastní upozornění.\n\nPOUZE PRO POKROČILÉ UŽIVATELE
Upozornění o nadcházející lekci
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 83bbe307f..9a6e3e662 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -182,6 +182,17 @@
Zusätzliche Lektionen
Zusätzliche Lektionen anzeigen
Keine Informationen über zusätzlichen Lektionen
+ New lesson
+ New additional lesson
+ Additional lesson added successfully
+ Additional lesson deleted successfully
+ Repeat weekly
+ Delete additional lesson
+ Just this lesson
+ All in the series
+ Start time
+ End time
+ End time must be greater than start time
Übersicht über die Schulbesuch
Aus schulischen Gründen abwesend
@@ -582,7 +593,9 @@
Ihr Gerät hat möglicherweise Probleme mit der Datensynchronisierung und Benachrichtigungen.\n\nUm diese zu reparieren, fügen Sie Wulkanowy zum Autostart hinzu und deaktivieren Sie die Batterieoptimierung in den Systemeinstellungen des Geräts.
Debug-Benachrichtigungen anzeigen
Synchronisierung ist deaktiviert
+ Official app notifications
Offizielle App-Benachrichtigungen erfassen
+ Remove official app notifications after capture
Benachrichtigungen erfassen
With this feature you can gain a substitute of push notifications like in the official app. All you need to do is allow Wulkanowy to receive all notifications in your system settings.\n\nHow it works?\nWhen you get a notification in Dziennik VULCAN, Wulkanowy will be notified (that\'s what these extra permissions are for) and will trigger a sync so that can send its own notification.\n\nFOR ADVANCED USERS ONLY
Upcoming lesson notifications
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 17fcb0884..32862c01f 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -204,6 +204,17 @@
Dodatkowe lekcje
Pokaż dodatkowe lekcje
Brak informacji o dodatkowych lekcjach
+ Nowa lekcja
+ Nowa dodatkowa lekcja
+ Dodatkowa lekcja dodana pomyślnie
+ Dodatkowa lekcja usunięta pomyślnie
+ Powtarzaj co tydzień
+ Usuń dodatkową lekcję
+ Tylko ta lekcja
+ Wszystkie w serii
+ Godzina rozpoczęcia
+ Godzina zakończenia
+ Godzina zakończenia musi być późniejsza niż godzina rozpoczęcia
Podsumowanie frekwencji
Nieobecność z przyczyn szkolnych
@@ -668,7 +679,9 @@
Na twoim urządzeniu mogą występować problemy z synchronizacją danych i powiadomieniami.\n\nBy je naprawić, dodaj Wulkanowego do autostartu i wyłącz optymalizację/oszczędzanie baterii w ustawieniach systemowych telefonu.
Pokazuj powiadomienia debugowania
Synchronizacja jest wyłączona
+ Official app notifications
Przechwytywanie powiadomień oficjalnej aplikacji
+ Remove official app notifications after capture
Przechwytywanie powiadomień
Dzięki tej funkcji możesz uzyskać namiastkę powiadomień push, takich jak w oficjalnej aplikacji. Wszystko, co musisz zrobić, to zezwolić Wulkanowemu na odczytywanie wszystkich powiadomień w ustawieniach systemowych.\n\nJak to działa?\nKiedy otrzymasz powiadomienie w Dzienniczku VULCAN, Wulkanowy zostanie o tym powiadomiony (do tego jest to dodatkowe uprawnienie) i uruchomi synchronizację, aby mógł wysłać własne powiadomienie.\n\nWYŁĄCZNIE DLA ZAAWANSOWANYCH UŻYTKOWNIKÓW
Powiadomienia o nadchodzących lekcjach
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index e09e6d7da..87cae3ea7 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -204,6 +204,17 @@
Дополнительные уроки
Показать дополнительные уроки
Нет информации о дополнительных уроках
+ New lesson
+ New additional lesson
+ Additional lesson added successfully
+ Additional lesson deleted successfully
+ Repeat weekly
+ Delete additional lesson
+ Just this lesson
+ All in the series
+ Start time
+ End time
+ End time must be greater than start time
Итоговая посещаемость
Отсутствие по школьным причинам
@@ -668,7 +679,9 @@
На вашем устройстве могут быть проблемы с синхронизацией данных и уведомлениями.\n\nЧтобы их исправить, вам необходимо добавить Wulkanowy в авто-старт и выключить оптимизацию/экономию батареи в настройках устройства.
Показывать дебаг-уведомления
Синхронизация отключена
+ Official app notifications
Записывать официальные уведомления
+ Remove official app notifications after capture
Показывать push-уведомления
С помощью этой функции вы можете получить замену push-уведомлений, как в официальном приложении. Все, что вам нужно сделать, это разрешить Wulkanowy получать все уведомления в настройках системы.\n\nКак это работает?\nКогда вы получаете уведомление в Dziennik VULCAN, Wulkanowy будет уведомлен (это требует дополнительных прав) и запустит синхронизацию, чтобы отправить свое уведомление.\n\nТОЛЬКО ДЛЯ ПОЛЬЗОВАТЕЛЯ
Показывать уведомления о будущих уроках
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 584a923d5..7899b5b59 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -204,6 +204,17 @@
Ďalšie lekcie
Zobraziť ďalšie lekcie
Žiadne informácie o ďalších lekciách
+ Nová lekcia
+ Nová ďalšia lekcia
+ Ďalšia lekcia bola úspešne pridaná
+ Ďalšia lekcia bola úspešne odstránená
+ Opakovať každý týždeň
+ Odstrániť ďalšiu lekciu
+ Iba táto lekcia
+ Všetky v sérii
+ Čas začatia
+ Čas ukončenia
+ Čas ukončenia musí byť neskorší ako čas začatia
Zhrnutie frekvencií
Neprítomnosť zo školských dôvodov
@@ -668,7 +679,9 @@
Vaše zariadenie môže mať problémy so synchronizáciou dát as upozorneniami.\n\nAk ich chcete opraviť, pridajte Wulkanového do funkcie Autostart a vypnite optimalizáciu/úsporu batérie v nastavení systému telefóne.
Zobraziť upozornenia o ladení
Synchronizácia je vypnutá
+ Official app notifications
Zachytiť upozornenia oficiálnej aplikácie
+ Remove official app notifications after capture
Zachytiť upozornenia
S touto funkciou môžete získať náhradu push upozornení ako v oficiálnej aplikácii. Všetko, čo musíte urobiť, je povoliť Wulkanowému čítať všetky vaše upozornenia v nastaveniach systému.\n\nAko to funguje?\nKeď dostanete oznámenie v Deníčku VULCAN, Wulkanowy bude o tom informovaný (k tomu je to dodatočné povolenie) a spustí synchronizáciu, aby mohol zaslať vlastné upozornenie.\n\nLEN PRE POKROČILÝCH POUŽĺVATEĹOV
Upozornenia o nadchádzajúcej lekciu
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 5d24fedf2..f3f749cf9 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -204,6 +204,17 @@
Додаткові уроки
Показати додаткові уроки
Немає інформації про додаткових уроків
+ New lesson
+ New additional lesson
+ Additional lesson added successfully
+ Additional lesson deleted successfully
+ Repeat weekly
+ Delete additional lesson
+ Just this lesson
+ All in the series
+ Start time
+ End time
+ End time must be greater than start time
Підсумок відвідуваності
Відсутність зі шкільних причин
@@ -668,7 +679,9 @@
На вашому пристрої можуть бути помилки з синхронізацією і повідомленнями\n\nЩоб виправити іх, вам необхідно додати Wulkanowy в авто-старт и вимкнути оптимізацію/экономію батареї в налаштуваннях пристрою.
Показувати дебаг-повідомлення
Синхронізація вимкнена
+ Official app notifications
Захоплювати офіційні сповіщення програм
+ Remove official app notifications after capture
Показувати push-повідомлення
За допомогою цієї функції ви можете отримати заміну push -повідомлень, як у офіційному додатку. Все, що вам потрібно зробити, це дозволити Wulkanowy отримувати всі сповіщення у налаштуваннях вашої системи. \ N \ nЯк це працює? \ NКоли ви отримаєте сповіщення у Dziennik VULCAN, Wulkanowy отримає сповіщення (для цього призначені ці додаткові дозволи) і запустить синхронізація, яка може надсилати власне сповіщення. \ n \ n ТІЛЬКИ ДЛЯ РОЗШИРЕНИХ КОРИСТУВАЧІВ
Показувати повідомлення о наступних уроках
From 5e9691750843d9a295b88689ebbe2614322bd886 Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Tue, 28 Dec 2021 12:16:52 +0100
Subject: [PATCH 014/669] Fix overlapping text in the error dialog (#1708)
---
.../wulkanowy/services/sync/SyncWorker.kt | 42 +++---
.../github/wulkanowy/ui/base/ErrorDialog.kt | 112 ++++++++--------
.../modules/dashboard/DashboardPresenter.kt | 4 +-
.../ui/modules/settings/sync/SyncFragment.kt | 2 +-
.../ui/modules/settings/sync/SyncPresenter.kt | 5 +-
app/src/main/res/layout/dialog_error.xml | 126 +++---------------
6 files changed, 111 insertions(+), 180 deletions(-)
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 52979e635..a2d1dd57e 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
@@ -38,13 +38,18 @@ class SyncWorker @AssistedInject constructor(
private val dispatchersProvider: DispatchersProvider
) : CoroutineWorker(appContext, workerParameters) {
- override suspend fun doWork() = withContext(dispatchersProvider.io) {
+ override suspend fun doWork(): Result = withContext(dispatchersProvider.io) {
Timber.i("SyncWorker is starting")
if (!studentRepository.isCurrentStudentSet()) return@withContext Result.failure()
- val student = studentRepository.getCurrentStudent()
- val semester = semesterRepository.getCurrentSemester(student, true)
+ val (student, semester) = try {
+ val student = studentRepository.getCurrentStudent()
+ val semester = semesterRepository.getCurrentSemester(student, true)
+ student to semester
+ } catch (e: Throwable) {
+ return@withContext getResultFromErrors(listOf(e))
+ }
val exceptions = works.mapNotNull { work ->
try {
@@ -62,20 +67,7 @@ class SyncWorker @AssistedInject constructor(
}
}
}
- val result = when {
- exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
- Result.failure(
- Data.Builder()
- .putString("error", exceptions.map { it.stackTraceToString() }.toString())
- .build()
- )
- }
- exceptions.isNotEmpty() -> Result.retry()
- else -> {
- preferencesRepository.lasSyncDate = LocalDateTime.now()
- Result.success()
- }
- }
+ val result = getResultFromErrors(exceptions)
if (preferencesRepository.isDebugNotificationEnable) notify(result)
Timber.i("SyncWorker result: $result")
@@ -83,6 +75,22 @@ class SyncWorker @AssistedInject constructor(
return@withContext result
}
+ private fun getResultFromErrors(errors: List): Result = when {
+ errors.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
+ Result.failure(
+ Data.Builder()
+ .putString("error_message", errors.joinToString { it.message.toString() })
+ .putString("error_stack", errors.map { it.stackTraceToString() }.toString())
+ .build()
+ )
+ }
+ errors.isNotEmpty() -> Result.retry()
+ else -> {
+ preferencesRepository.lasSyncDate = LocalDateTime.now()
+ Result.success()
+ }
+ }
+
private fun notify(result: Result) {
notificationManager.notify(
Random.nextInt(Int.MAX_VALUE),
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
index 4c279d816..c2ffff1f5 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
@@ -1,28 +1,25 @@
package io.github.wulkanowy.ui.base
+import android.app.Dialog
import android.content.ClipData
import android.content.ClipboardManager
import android.os.Bundle
import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.HorizontalScrollView
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.appcompat.app.AlertDialog
import androidx.core.content.getSystemService
+import androidx.core.os.bundleOf
import androidx.core.view.isGone
+import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.DialogErrorBinding
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
-import io.github.wulkanowy.utils.AppInfo
-import io.github.wulkanowy.utils.getString
-import io.github.wulkanowy.utils.openAppInMarket
-import io.github.wulkanowy.utils.openEmailClient
-import io.github.wulkanowy.utils.openInternetBrowser
+import io.github.wulkanowy.utils.*
import okhttp3.internal.http2.StreamResetException
import java.io.InterruptedIOException
import java.net.ConnectException
@@ -31,72 +28,77 @@ import java.net.UnknownHostException
import javax.inject.Inject
@AndroidEntryPoint
-class ErrorDialog : BaseDialogFragment() {
-
- private lateinit var error: Throwable
+class ErrorDialog : DialogFragment() {
@Inject
lateinit var appInfo: AppInfo
companion object {
- private const val ARGUMENT_KEY = "Data"
+ private const val ARGUMENT_KEY = "error"
fun newInstance(error: Throwable) = ErrorDialog().apply {
- arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, error) }
+ arguments = bundleOf(ARGUMENT_KEY to error)
}
}
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setStyle(STYLE_NO_TITLE, 0)
- arguments?.run {
- error = getSerializable(ARGUMENT_KEY) as Throwable
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val error = requireArguments().getSerializable(ARGUMENT_KEY) as Throwable
+
+ val binding = DialogErrorBinding.inflate(LayoutInflater.from(context))
+ binding.bindErrorDetails(error)
+
+ return getAlertDialog(binding, error).apply {
+ enableReportButtonIfErrorIsReportable(error)
}
}
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = DialogErrorBinding.inflate(inflater).apply { binding = this }.root
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- val errorStacktrace = error.stackTraceToString()
-
- with(binding) {
- errorDialogContent.text = errorStacktrace.replace(": ${error.localizedMessage}", "")
- with(errorDialogHorizontalScroll) {
- post { fullScroll(HorizontalScrollView.FOCUS_LEFT) }
- }
- errorDialogCopy.setOnClickListener {
- val clip = ClipData.newPlainText("Error details", errorStacktrace)
- activity?.getSystemService()?.setPrimaryClip(clip)
-
- Toast.makeText(context, R.string.all_copied, LENGTH_LONG).show()
- }
- errorDialogCancel.setOnClickListener { dismiss() }
- errorDialogReport.setOnClickListener {
+ private fun getAlertDialog(binding: DialogErrorBinding, error: Throwable): AlertDialog {
+ return MaterialAlertDialogBuilder(requireContext()).apply {
+ val errorStacktrace = error.stackTraceToString()
+ setTitle(R.string.all_details)
+ setView(binding.root)
+ setNeutralButton(R.string.about_feedback) { _, _ ->
openConfirmDialog { openEmailClient(errorStacktrace) }
}
+ setNegativeButton(android.R.string.cancel) { _, _ -> }
+ setPositiveButton(android.R.string.copy) { _, _ -> copyErrorToClipboard(errorStacktrace) }
+ }.create()
+ }
+
+ private fun DialogErrorBinding.bindErrorDetails(error: Throwable) {
+ return with(this) {
errorDialogHumanizedMessage.text = resources.getString(error)
errorDialogErrorMessage.text = error.localizedMessage
errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank()
- errorDialogReport.isEnabled = when (error) {
- is UnknownHostException,
- is InterruptedIOException,
- is ConnectException,
- is StreamResetException,
- is SocketTimeoutException,
- is ServiceUnavailableException,
- is FeatureDisabledException,
- is FeatureNotAvailableException -> false
- else -> true
- }
+ errorDialogContent.text = error.stackTraceToString()
+ .replace(": ${error.localizedMessage}", "")
}
}
+ private fun AlertDialog.enableReportButtonIfErrorIsReportable(error: Throwable) {
+ setOnShowListener {
+ getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = isErrorShouldBeReported(error)
+ }
+ }
+
+ private fun isErrorShouldBeReported(error: Throwable): Boolean = when (error) {
+ is UnknownHostException,
+ is InterruptedIOException,
+ is ConnectException,
+ is StreamResetException,
+ is SocketTimeoutException,
+ is ServiceUnavailableException,
+ is FeatureDisabledException,
+ is FeatureNotAvailableException -> false
+ else -> true
+ }
+
+ private fun copyErrorToClipboard(errorStacktrace: String) {
+ val clip = ClipData.newPlainText("Error details", errorStacktrace)
+ requireActivity().getSystemService()?.setPrimaryClip(clip)
+ Toast.makeText(requireContext(), R.string.all_copied, LENGTH_LONG).show()
+ }
+
private fun openConfirmDialog(callback: () -> Unit) {
AlertDialog.Builder(requireContext())
.setTitle(R.string.dialog_error_check_update)
@@ -127,4 +129,8 @@ class ErrorDialog : BaseDialogFragment() {
}
)
}
+
+ private fun showMessage(text: String) {
+ Toast.makeText(requireContext(), text, LENGTH_LONG).show()
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index d081e19aa..7ba4c4b6f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -716,7 +716,7 @@ class DashboardPresenter @Inject constructor(
itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null
val isGeneralError =
filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError
- val errorMessage = itemsLoadedList.map { it.error?.stackTraceToString() }.toString()
+ val firstError = itemsLoadedList.mapNotNull { it.error }.firstOrNull()
val filteredOriginalLoadedList =
dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT }
@@ -726,7 +726,7 @@ class DashboardPresenter @Inject constructor(
filteredOriginalLoadedList.none { it.error == null } && filteredOriginalLoadedList.isNotEmpty() || wasAccountItemError
if (isGeneralError && isItemsLoaded) {
- lastError = Exception(errorMessage)
+ lastError = requireNotNull(firstError)
view?.run {
showProgress(false)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt
index 160b7c37b..d81c35d39 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt
@@ -91,7 +91,7 @@ class SyncFragment : PreferenceFragmentCompat(),
}
override fun showErrorDetailsDialog(error: Throwable) {
- ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
+ ErrorDialog.newInstance(error).show(childFragmentManager, "error_details")
}
override fun onResume() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt
index 0d404a138..fc47e29ab 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt
@@ -59,7 +59,10 @@ class SyncPresenter @Inject constructor(
WorkInfo.State.FAILED -> {
showError(
syncFailedString,
- Throwable(workInfo.outputData.getString("error"))
+ Throwable(
+ message = workInfo.outputData.getString("error_message"),
+ cause = Throwable(workInfo.outputData.getString("error_stack"))
+ )
)
analytics.logEvent("sync_now", "status" to "failed")
}
diff --git a/app/src/main/res/layout/dialog_error.xml b/app/src/main/res/layout/dialog_error.xml
index b52c99aca..98b9c8b16 100644
--- a/app/src/main/res/layout/dialog_error.xml
+++ b/app/src/main/res/layout/dialog_error.xml
@@ -7,15 +7,6 @@
android:orientation="vertical"
tools:context=".ui.base.ErrorDialog">
-
-
-
+ android:layout_height="200dp"
+ android:overScrollMode="ifContentScrolls"
+ android:paddingHorizontal="24dp"
+ app:layout_constraintTop_toTopOf="parent">
-
+ android:layout_height="wrap_content">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
From 684c258e2d817525cd4a26ea23233ffbefb032c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Tue, 28 Dec 2021 18:56:59 +0100
Subject: [PATCH 015/669] Remove admin message offline first cache (#1735)
---
.../repositories/AdminMessageRepository.kt | 13 +++-------
.../modules/dashboard/DashboardPresenter.kt | 26 +++----------------
2 files changed, 6 insertions(+), 33 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt
index 1b17e3bf3..e455411ea 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt
@@ -4,7 +4,6 @@ import io.github.wulkanowy.data.api.AdminMessageService
import io.github.wulkanowy.data.db.dao.AdminMessageDao
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.AppInfo
-import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.networkBoundResource
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
@@ -14,23 +13,17 @@ import javax.inject.Singleton
class AdminMessageRepository @Inject constructor(
private val adminMessageService: AdminMessageService,
private val adminMessageDao: AdminMessageDao,
- private val appInfo: AppInfo,
- private val refreshHelper: AutoRefreshHelper,
+ private val appInfo: AppInfo
) {
private val saveFetchResultMutex = Mutex()
- private val cacheKey = "admin_messages"
-
- suspend fun getAdminMessages(student: Student, forceRefresh: Boolean) = networkBoundResource(
+ suspend fun getAdminMessages(student: Student) = networkBoundResource(
mutex = saveFetchResultMutex,
query = { adminMessageDao.loadAll() },
fetch = { adminMessageService.getAdminMessages() },
- shouldFetch = {
- refreshHelper.shouldBeRefreshed(cacheKey) || forceRefresh
- },
+ shouldFetch = { true },
saveFetchResult = { oldItems, newItems ->
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
- refreshHelper.updateLastRefreshTimestamp(cacheKey)
},
showSavedOnLoading = false,
mapResult = { adminMessages ->
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index 7ba4c4b6f..80340c08a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -6,33 +6,13 @@ import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder
-import io.github.wulkanowy.data.repositories.AdminMessageRepository
-import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
-import io.github.wulkanowy.data.repositories.ConferenceRepository
-import io.github.wulkanowy.data.repositories.ExamRepository
-import io.github.wulkanowy.data.repositories.GradeRepository
-import io.github.wulkanowy.data.repositories.HomeworkRepository
-import io.github.wulkanowy.data.repositories.LuckyNumberRepository
-import io.github.wulkanowy.data.repositories.MessageRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
-import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
-import io.github.wulkanowy.data.repositories.SemesterRepository
-import io.github.wulkanowy.data.repositories.StudentRepository
-import io.github.wulkanowy.data.repositories.TimetableRepository
+import io.github.wulkanowy.data.repositories.*
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.calculatePercentage
import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.nextOrSameSchoolDay
-import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.emitAll
-import kotlinx.coroutines.flow.filterNot
-import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.time.LocalDate
@@ -582,7 +562,7 @@ class DashboardPresenter @Inject constructor(
}
private fun loadAdminMessage(student: Student, forceRefresh: Boolean) {
- flowWithResourceIn { adminMessageRepository.getAdminMessages(student, forceRefresh) }
+ flowWithResourceIn { adminMessageRepository.getAdminMessages(student) }
.map {
val isDismissed = it.data?.id in preferencesRepository.dismissedAdminMessageIds
it.copy(data = it.data.takeUnless { isDismissed })
From 496641f594cdef4ab882dadeab03fa01b0908877 Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Wed, 29 Dec 2021 08:31:43 +0100
Subject: [PATCH 016/669] Fix notification spam after login (#1715)
---
.../io/github/wulkanowy/services/sync/SyncManager.kt | 4 +++-
.../io/github/wulkanowy/services/sync/SyncWorker.kt | 7 ++++++-
.../services/sync/works/AttendanceSummaryWork.kt | 9 +++++++--
.../wulkanowy/services/sync/works/AttendanceWork.kt | 6 ++----
.../services/sync/works/CompletedLessonWork.kt | 10 ++++++++--
.../wulkanowy/services/sync/works/ConferenceWork.kt | 6 ++----
.../github/wulkanowy/services/sync/works/ExamWork.kt | 6 ++----
.../services/sync/works/GradeStatisticsWork.kt | 2 +-
.../github/wulkanowy/services/sync/works/GradeWork.kt | 6 ++----
.../wulkanowy/services/sync/works/HomeworkWork.kt | 6 ++----
.../wulkanowy/services/sync/works/LuckyNumberWork.kt | 6 ++----
.../wulkanowy/services/sync/works/MessageWork.kt | 6 ++----
.../github/wulkanowy/services/sync/works/NoteWork.kt | 6 ++----
.../wulkanowy/services/sync/works/RecipientWork.kt | 2 +-
.../services/sync/works/SchoolAnnouncementWork.kt | 6 ++----
.../wulkanowy/services/sync/works/TeacherWork.kt | 2 +-
.../wulkanowy/services/sync/works/TimetableWork.kt | 6 ++----
.../io/github/wulkanowy/services/sync/works/Work.kt | 2 +-
.../login/studentselect/LoginStudentSelectPresenter.kt | 3 +++
.../studentselect/LoginStudentSelectPresenterTest.kt | 6 +++++-
20 files changed, 56 insertions(+), 51 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt
index 32ca20afc..c1bed4dd3 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt
@@ -74,10 +74,12 @@ class SyncManager @Inject constructor(
}
}
- fun startOneTimeSyncWorker(): Flow {
+ // if quiet, no notifications will be sent
+ fun startOneTimeSyncWorker(quiet: Boolean = false): Flow {
val work = OneTimeWorkRequestBuilder()
.setInputData(
Data.Builder()
+ .putBoolean("quiet", quiet)
.putBoolean("one_time", true)
.build()
)
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 a2d1dd57e..4a9bfd58d 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
@@ -54,7 +54,7 @@ class SyncWorker @AssistedInject constructor(
val exceptions = works.mapNotNull { work ->
try {
Timber.i("${work::class.java.simpleName} is starting")
- work.doWork(student, semester)
+ work.doWork(student, semester, isNotificationsEnabled())
Timber.i("${work::class.java.simpleName} result: Success")
null
} catch (e: Throwable) {
@@ -75,6 +75,11 @@ class SyncWorker @AssistedInject constructor(
return@withContext result
}
+ private fun isNotificationsEnabled(): Boolean {
+ val quiet = inputData.getBoolean("quiet", false)
+ return preferencesRepository.isNotificationsEnable && !quiet
+ }
+
private fun getResultFromErrors(errors: List): Result = when {
errors.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
Result.failure(
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt
index cbe1fe6bd..84b7017b1 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt
@@ -10,7 +10,12 @@ class AttendanceSummaryWork @Inject constructor(
private val attendanceSummaryRepository: AttendanceSummaryRepository
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
- attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, true).waitForResult()
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
+ attendanceSummaryRepository.getAttendanceSummary(
+ student = student,
+ semester = semester,
+ subjectId = -1,
+ forceRefresh = true,
+ ).waitForResult()
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt
index f7b680e31..9abf43e08 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt
@@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.AttendanceRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification
import io.github.wulkanowy.utils.previousOrSameSchoolDay
import io.github.wulkanowy.utils.waitForResult
@@ -14,17 +13,16 @@ import javax.inject.Inject
class AttendanceWork @Inject constructor(
private val attendanceRepository: AttendanceRepository,
private val newAttendanceNotification: NewAttendanceNotification,
- private val preferencesRepository: PreferencesRepository
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
attendanceRepository.getAttendance(
student = student,
semester = semester,
start = now().previousOrSameSchoolDay,
end = now().previousOrSameSchoolDay,
forceRefresh = true,
- notify = preferencesRepository.isNotificationsEnable
+ notify = notify,
)
.waitForResult()
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt
index 17bd61292..c6ada9446 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt
@@ -13,7 +13,13 @@ class CompletedLessonWork @Inject constructor(
private val completedLessonsRepository: CompletedLessonsRepository
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
- completedLessonsRepository.getCompletedLessons(student, semester, now().monday, now().sunday, true).waitForResult()
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
+ completedLessonsRepository.getCompletedLessons(
+ student = student,
+ semester = semester,
+ start = now().monday,
+ end = now().sunday,
+ forceRefresh = true,
+ ).waitForResult()
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt
index 002b4f764..becd74668 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt
@@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.ConferenceRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewConferenceNotification
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
@@ -11,16 +10,15 @@ import javax.inject.Inject
class ConferenceWork @Inject constructor(
private val conferenceRepository: ConferenceRepository,
- private val preferencesRepository: PreferencesRepository,
private val newConferenceNotification: NewConferenceNotification,
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
conferenceRepository.getConferences(
student = student,
semester = semester,
forceRefresh = true,
- notify = preferencesRepository.isNotificationsEnable
+ notify = notify
).waitForResult()
conferenceRepository.getConferenceFromDatabase(semester).first()
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt
index a1ce553a7..39579dc8c 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt
@@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.ExamRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewExamNotification
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
@@ -12,18 +11,17 @@ import javax.inject.Inject
class ExamWork @Inject constructor(
private val examRepository: ExamRepository,
- private val preferencesRepository: PreferencesRepository,
private val newExamNotification: NewExamNotification,
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
examRepository.getExams(
student = student,
semester = semester,
start = now(),
end = now(),
forceRefresh = true,
- notify = preferencesRepository.isNotificationsEnable
+ notify = notify,
).waitForResult()
examRepository.getExamsFromDatabase(semester, now()).first()
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt
index 4575b419b..2e915199e 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt
@@ -10,7 +10,7 @@ class GradeStatisticsWork @Inject constructor(
private val gradeStatisticsRepository: GradeStatisticsRepository
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
with(gradeStatisticsRepository) {
getGradesPartialStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()
getGradesSemesterStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt
index 0932405eb..dd49f143c 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt
@@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.GradeRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewGradeNotification
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
@@ -11,16 +10,15 @@ import javax.inject.Inject
class GradeWork @Inject constructor(
private val gradeRepository: GradeRepository,
- private val preferencesRepository: PreferencesRepository,
private val newGradeNotification: NewGradeNotification,
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
gradeRepository.getGrades(
student = student,
semester = semester,
forceRefresh = true,
- notify = preferencesRepository.isNotificationsEnable
+ notify = notify,
).waitForResult()
gradeRepository.getGradesFromDatabase(semester).first()
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt
index 2a5d2d7c0..1385191b7 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt
@@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.HomeworkRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewHomeworkNotification
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.waitForResult
@@ -13,18 +12,17 @@ import javax.inject.Inject
class HomeworkWork @Inject constructor(
private val homeworkRepository: HomeworkRepository,
- private val preferencesRepository: PreferencesRepository,
private val newHomeworkNotification: NewHomeworkNotification,
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
homeworkRepository.getHomework(
student = student,
semester = semester,
start = now().nextOrSameSchoolDay,
end = now().nextOrSameSchoolDay,
forceRefresh = true,
- notify = preferencesRepository.isNotificationsEnable
+ notify = notify,
).waitForResult()
homeworkRepository.getHomeworkFromDatabase(semester, now(), now().plusDays(7)).first()
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt
index 348f92142..f223a8546 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt
@@ -3,22 +3,20 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewLuckyNumberNotification
import io.github.wulkanowy.utils.waitForResult
import javax.inject.Inject
class LuckyNumberWork @Inject constructor(
private val luckyNumberRepository: LuckyNumberRepository,
- private val preferencesRepository: PreferencesRepository,
private val newLuckyNumberNotification: NewLuckyNumberNotification,
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
luckyNumberRepository.getLuckyNumber(
student = student,
forceRefresh = true,
- notify = preferencesRepository.isNotificationsEnable
+ notify = notify,
).waitForResult()
luckyNumberRepository.getNotNotifiedLuckyNumber(student)?.let {
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 b5624a76d..5bf326c7b 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
@@ -4,7 +4,6 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
import io.github.wulkanowy.data.repositories.MessageRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewMessageNotification
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
@@ -12,17 +11,16 @@ import javax.inject.Inject
class MessageWork @Inject constructor(
private val messageRepository: MessageRepository,
- private val preferencesRepository: PreferencesRepository,
private val newMessageNotification: NewMessageNotification,
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
messageRepository.getMessages(
student = student,
semester = semester,
folder = RECEIVED,
forceRefresh = true,
- notify = preferencesRepository.isNotificationsEnable
+ notify = notify
).waitForResult()
messageRepository.getMessagesFromDatabase(student).first()
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt
index 6f18eddf1..d66c3d661 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt
@@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.NoteRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.services.sync.notifications.NewNoteNotification
import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
@@ -11,16 +10,15 @@ import javax.inject.Inject
class NoteWork @Inject constructor(
private val noteRepository: NoteRepository,
- private val preferencesRepository: PreferencesRepository,
private val newNoteNotification: NewNoteNotification,
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
noteRepository.getNotes(
student = student,
semester = semester,
forceRefresh = true,
- notify = preferencesRepository.isNotificationsEnable
+ notify = notify,
).waitForResult()
noteRepository.getNotesFromDatabase(student).first()
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt
index 34ab3db04..425e68b91 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt
@@ -11,7 +11,7 @@ class RecipientWork @Inject constructor(
private val recipientRepository: RecipientRepository
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
reportingUnitRepository.refreshReportingUnits(student)
reportingUnitRepository.getReportingUnits(student).let { units ->
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt
index 268992f46..9cee59024 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt
@@ -2,7 +2,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification
import io.github.wulkanowy.utils.waitForResult
@@ -11,15 +10,14 @@ import javax.inject.Inject
class SchoolAnnouncementWork @Inject constructor(
private val schoolAnnouncementRepository: SchoolAnnouncementRepository,
- private val preferencesRepository: PreferencesRepository,
private val newSchoolAnnouncementNotification: NewSchoolAnnouncementNotification,
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
schoolAnnouncementRepository.getSchoolAnnouncements(
student = student,
forceRefresh = true,
- notify = preferencesRepository.isNotificationsEnable
+ notify = notify,
).waitForResult()
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt
index 7c614c6c5..751fb6cc7 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt
@@ -8,7 +8,7 @@ import javax.inject.Inject
class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
teacherRepository.getTeachers(student, semester, true).waitForResult()
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt
index fcc330638..575f9b961 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt
@@ -2,7 +2,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification
import io.github.wulkanowy.utils.nextOrSameSchoolDay
@@ -14,17 +13,16 @@ import javax.inject.Inject
class TimetableWork @Inject constructor(
private val timetableRepository: TimetableRepository,
private val changeTimetableNotification: ChangeTimetableNotification,
- private val preferencesRepository: PreferencesRepository
) : Work {
- override suspend fun doWork(student: Student, semester: Semester) {
+ override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
timetableRepository.getTimetable(
student = student,
semester = semester,
start = now().nextOrSameSchoolDay,
end = now().nextOrSameSchoolDay,
forceRefresh = true,
- notify = preferencesRepository.isNotificationsEnable
+ notify = notify,
)
.waitForResult()
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt
index c41f41ce2..1c0214cdd 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/Work.kt
@@ -5,5 +5,5 @@ import io.github.wulkanowy.data.db.entities.Student
interface Work {
- suspend fun doWork(student: Student, semester: Semester)
+ suspend fun doWork(student: Student, semester: Semester, notify: Boolean)
}
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 8c475a672..71c60e62d 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
@@ -4,6 +4,7 @@ import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
@@ -16,6 +17,7 @@ import javax.inject.Inject
class LoginStudentSelectPresenter @Inject constructor(
studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler,
+ private val syncManager: SyncManager,
private val analytics: AnalyticsHelper
) : BasePresenter(loginErrorHandler, studentRepository) {
@@ -97,6 +99,7 @@ class LoginStudentSelectPresenter @Inject constructor(
}
Status.SUCCESS -> {
Timber.i("Registration result: Success")
+ syncManager.startOneTimeSyncWorker(quiet = true)
view?.openMainView()
logRegisterEvent(studentsWithSemesters)
}
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 3be30827a..1ec885904 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
@@ -4,6 +4,7 @@ import io.github.wulkanowy.MainCoroutineRule
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.mockk.MockKAnnotations
@@ -36,6 +37,9 @@ class LoginStudentSelectPresenterTest {
@MockK(relaxed = true)
lateinit var analytics: AnalyticsHelper
+ @MockK(relaxed = true)
+ lateinit var syncManager: SyncManager
+
private lateinit var presenter: LoginStudentSelectPresenter
private val testStudent by lazy {
@@ -77,7 +81,7 @@ class LoginStudentSelectPresenterTest {
every { loginStudentSelectView.showProgress(any()) } just Runs
every { loginStudentSelectView.showContent(any()) } just Runs
- presenter = LoginStudentSelectPresenter(studentRepository, errorHandler, analytics)
+ presenter = LoginStudentSelectPresenter(studentRepository, errorHandler, syncManager, analytics)
presenter.onAttachView(loginStudentSelectView, emptyList())
}
From 0965d03f1a664bd4de69e58d3ad88ddee415be8b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Wed, 29 Dec 2021 11:57:17 +0100
Subject: [PATCH 017/669] New Crowdin updates (#1734)
---
app/src/main/res/values-cs/strings.xml | 42 +++++++++++++-------------
app/src/main/res/values-pl/strings.xml | 4 +--
app/src/main/res/values-sk/strings.xml | 40 ++++++++++++------------
3 files changed, 43 insertions(+), 43 deletions(-)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 330b75784..3d9cdd9a7 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -12,7 +12,7 @@
O aplikaci
Prohlížeč protokolů
Ladění
- Ladění upozornění
+ Ladění oznámení
Tvůrci
Licence
Zprávy
@@ -25,7 +25,7 @@
Podrobnosti účtu
Informace o žáku
Domů
- Centrum upozornění
+ Centrum oznámení
Semestr %1$d, %2$d/%3$d
@@ -668,23 +668,23 @@
Známky barevné schéma
Třídění předmětů
Jazyk
- Upozornění
+ Oznámení
Jiné
- Zobrazit upozornění
- Zobrazit upozornění o nadcházející lekci
- Nastavit upozornění o nadcházející lekci jako trvalé
- Vypnout, když upozornění není ve vašem hodinkách/náramku viditelné
- Otevřít systémová nastavení upozornění
- Opravte problémy se synchronizací a upozorněním
- Vaše zařízení může mít problémy se synchronizací dat as upozorněními.\n\nChcete-li je opravit, přidejte Wulkanového do funkce Autostart a vypněte optimalizaci/úsporu baterie v nastavení systému telefonu.
- Zobrazit upozornění o ladění
+ Zobrazit oznámení
+ Zobrazit oznámení o nadcházející lekci
+ Nastavit oznámení o nadcházející lekci jako trvalé
+ Vypnout, když oznámení není ve vašem hodinkách/náramku viditelné
+ Otevřít systémová nastavení oznámení
+ Opravte problémy se synchronizací a oznámením
+ Vaše zařízení může mít problémy se synchronizací dat as oznámeními.\n\nChcete-li je opravit, přidejte Wulkanového do funkce Autostart a vypněte optimalizaci/úsporu baterie v nastavení systému telefonu.
+ Zobrazit oznámení o ladění
Synchronizace je vypnutá
- Official app notifications
- Zachytit upozornění oficiální aplikací
- Remove official app notifications after capture
- Zachytit upozornění
- S touto funkcí můžete získat náhradu push upozornění jako v oficiální aplikaci. Vše, co musíte udělat, je povolit Wulkanowému číst všechna vaše upozornění v nastaveních systému.\n\nJak to funguje?\nKdyž obdržíte oznámení v Deníčku VULCAN, Wulkanowy bude o tom informován (k tomu je to dodatečné povolení) a spustí synchronizaci, aby mohl zaslat vlastní upozornění.\n\nPOUZE PRO POKROČILÉ UŽIVATELE
- Upozornění o nadcházející lekci
+ Oznámení oficiální aplikace
+ Zachytit oznámení oficiální aplikací
+ Odstranit oznámení oficiální aplikace po zachycení
+ Zachytit oznámení
+ S touto funkcí můžete získat náhradu push oznámení jako v oficiální aplikaci. Vše, co musíte udělat, je povolit Wulkanowému číst všechna vaše oznámení v nastaveních systému.\n\nJak to funguje?\nKdyž obdržíte oznámení v Deníčku VULCAN, Wulkanowy bude o tom informován (k tomu je to dodatečné povolení) a spustí synchronizaci, aby mohl zaslat vlastní oznámení.\n\nPOUZE PRO POKROČILÉ UŽIVATELE
+ Oznámení o nadcházející lekci
Musíte povolit Wulkanovému nastavit budíky a připomenutí v nastavení vašeho systému pro použití této funkce.
Přejít do nastavení
Synchronizace
@@ -711,7 +711,7 @@
Děkujeme za vaši podporu, vraťte se později pro více reklam
Pokročilé
Vzhled a chování
- Upozornění
+ Oznámení
Synchronizace
Reklamy
Známky
@@ -724,8 +724,8 @@
Zprávy
Vzhled a chování
Jazyky, motivy, třídění předmětů
- Upozornění aplikace, oprava problémů
- Upozornění
+ Oznámení aplikací, oprava problémů
+ Oznámení
Synchronizace
Automatická aktualizace, interval aktualizací
Hodnota plusu a mínusu, výpočet průměru
@@ -741,7 +741,7 @@
Nové zprávy
Nové poznámky
Nové školní oznámení
- Push upozornění
+ Push oznámení
Nadcházející lekce
Ladění
Změny plánu lekcí
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 32862c01f..ff21b6446 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -679,9 +679,9 @@
Na twoim urządzeniu mogą występować problemy z synchronizacją danych i powiadomieniami.\n\nBy je naprawić, dodaj Wulkanowego do autostartu i wyłącz optymalizację/oszczędzanie baterii w ustawieniach systemowych telefonu.
Pokazuj powiadomienia debugowania
Synchronizacja jest wyłączona
- Official app notifications
+ Powiadomienia oficjalnej aplikacji
Przechwytywanie powiadomień oficjalnej aplikacji
- Remove official app notifications after capture
+ Usuwaj powiadomienia oficjalnej aplikacji po przechwyceniu
Przechwytywanie powiadomień
Dzięki tej funkcji możesz uzyskać namiastkę powiadomień push, takich jak w oficjalnej aplikacji. Wszystko, co musisz zrobić, to zezwolić Wulkanowemu na odczytywanie wszystkich powiadomień w ustawieniach systemowych.\n\nJak to działa?\nKiedy otrzymasz powiadomienie w Dzienniczku VULCAN, Wulkanowy zostanie o tym powiadomiony (do tego jest to dodatkowe uprawnienie) i uruchomi synchronizację, aby mógł wysłać własne powiadomienie.\n\nWYŁĄCZNIE DLA ZAAWANSOWANYCH UŻYTKOWNIKÓW
Powiadomienia o nadchodzących lekcjach
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 7899b5b59..3698fce92 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -12,7 +12,7 @@
O aplikácii
Prehliadač protokolov
Ladenie
- Ladenie upozornení
+ Ladenie oznámení
Tvorcovia
Licencie
Správy
@@ -25,7 +25,7 @@
Podrobnosti účtu
Informácie o žiakovi
Domov
- Centrum upozornení
+ Centrum oznámení
Semester %1$d, %2$d/%3$d
@@ -668,23 +668,23 @@
Známky farebnú schému
Triedenie predmetov
Jazyk
- Upozornenia
+ Oznámenia
Iné
- Zobraziť upozornenia
- Zobraziť upozornenia o nadchádzajúcej lekciu
- Nastaviť upozornenia o nadchádzajúcej lekcii ako trvalé
- Vypnúť, keď upozornenia nie je vo vašom hodinkách/náramku viditeľné
- Otvoriť systémové nastavenia upozornení
- Opravte problémy so synchronizáciou a upozornením
- Vaše zariadenie môže mať problémy so synchronizáciou dát as upozorneniami.\n\nAk ich chcete opraviť, pridajte Wulkanového do funkcie Autostart a vypnite optimalizáciu/úsporu batérie v nastavení systému telefóne.
- Zobraziť upozornenia o ladení
+ Zobraziť oznámenia
+ Zobraziť oznámenia o nadchádzajúcich lekciách
+ Nastaviť oznámenia o nadchádzajúcej lekcií ako trvalé
+ Vypnúť, keď oznámenia nie je vo vašom hodinkách/náramku viditeľné
+ Otvoriť systémové nastavenia oznámení
+ Opravte problémy so synchronizáciou a oznámeniami
+ Vaše zariadenie môže mať problémy so synchronizáciou dát as oznámeniami.\n\nAk ich chcete opraviť, pridajte Wulkanového do funkcie Autostart a vypnite optimalizáciu/úsporu batérie v nastavení systému telefóne.
+ Zobraziť oznámenia o ladení
Synchronizácia je vypnutá
- Official app notifications
+ Oznámenia oficiálnej aplikácie
Zachytiť upozornenia oficiálnej aplikácie
- Remove official app notifications after capture
- Zachytiť upozornenia
- S touto funkciou môžete získať náhradu push upozornení ako v oficiálnej aplikácii. Všetko, čo musíte urobiť, je povoliť Wulkanowému čítať všetky vaše upozornenia v nastaveniach systému.\n\nAko to funguje?\nKeď dostanete oznámenie v Deníčku VULCAN, Wulkanowy bude o tom informovaný (k tomu je to dodatočné povolenie) a spustí synchronizáciu, aby mohol zaslať vlastné upozornenie.\n\nLEN PRE POKROČILÝCH POUŽĺVATEĹOV
- Upozornenia o nadchádzajúcej lekciu
+ Odstrániť oznámenia oficiálnej aplikácie po zachytení
+ Zachytiť oznámení
+ S touto funkciou môžete získať náhradu push oznámení ako v oficiálnej aplikácii. Všetko, čo musíte urobiť, je povoliť Wulkanowému čítať všetky vaše oznámenia v nastaveniach systému.\n\nAko to funguje?\nKeď dostanete oznámenie v Deníčku VULCAN, Wulkanowy bude o tom informovaný (k tomu je to dodatočné povolenie) a spustí synchronizáciu, aby mohol zaslať vlastné oznámenie.\n\nLEN PRE POKROČILÝCH POUŽĺVATEĹOV
+ Oznámenia o nadchádzajúcej lekcií
Musíte povoliť Wulkanovému nastaviť budíky a pripomenutie v nastavení vášho systému pre použitie tejto funkcie.
Prejsť do nastavení
Synchronizácia
@@ -711,7 +711,7 @@
Ďakujeme za vašu podporu, vráťte sa neskôr pre viac reklám
Pokročilé
Vzhľad a správanie
- Upozornenia
+ Oznámenia
Synchronizácia
Reklamy
Známky
@@ -724,8 +724,8 @@
Správy
Vzhľad a správanie
Jazyky, motívy, triedenie predmetov
- Upozornenia aplikácie, oprava problémov
- Upozornenia
+ Oznámenia aplikácie, oprava problémov
+ Oznámenia
Synchronizácia
Automatická aktualizácia, interval aktualizácií
Hodnota plusu a mínusu, výpočet priemeru
@@ -741,7 +741,7 @@
Nové správy
Nové poznámky
Nové školské oznámenia
- Push upozornenia
+ Push oznámenia
Nadchádzajúce lekcie
Ladenie
Zmeny plánu lekcií
From 68f0ecc45c7f29fb5ca2f1883be927129d10b5ff Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Wed, 29 Dec 2021 15:44:43 +0100
Subject: [PATCH 018/669] If only one student exists, don't show student name
in timetable notification (#1711)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Rafał Borcz
---
.../wulkanowy/data/db/dao/StudentDao.kt | 7 +--
.../data/repositories/StudentRepository.kt | 3 +
.../alarm/TimetableNotificationReceiver.kt | 55 ++++++++++++-------
.../notifications/AppNotificationManager.kt | 9 +--
4 files changed, 42 insertions(+), 32 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
index 3dda8a44b..853a7cb74 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
@@ -1,12 +1,7 @@
package io.github.wulkanowy.data.db.dao
-import androidx.room.Dao
-import androidx.room.Delete
-import androidx.room.Insert
+import androidx.room.*
import androidx.room.OnConflictStrategy.ABORT
-import androidx.room.Query
-import androidx.room.Transaction
-import androidx.room.Update
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt
index 9e4a1aabc..570f8bdb9 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt
@@ -128,4 +128,7 @@ class StudentRepository @Inject constructor(
suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) =
studentDb.update(studentNickAndAvatar)
+
+ suspend fun isOneUniqueStudent() = getSavedStudents(false)
+ .distinctBy { it.student.studentName }.size == 1
}
diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
index b388d2ac5..167060181 100644
--- a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
@@ -41,6 +41,8 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
const val NOTIFICATION_TYPE_UPCOMING = 2
const val NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION = 3
+ // FIXME only shows one notification even if there are multiple students.
+ // Probably want to fix after #721 is merged.
const val NOTIFICATION_ID = 2137
const val STUDENT_NAME = "student_name"
@@ -60,16 +62,21 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
Timber.d("Receiving intent... ${intent.toUri(0)}")
flowWithResource {
+ val showStudentName = !studentRepository.isOneUniqueStudent()
val student = studentRepository.getCurrentStudent(false)
val studentId = intent.getIntExtra(STUDENT_ID, 0)
- if (student.studentId == studentId) prepareNotification(context, intent)
- else Timber.d("Notification studentId($studentId) differs from current(${student.studentId})")
+
+ if (student.studentId == studentId) {
+ prepareNotification(context, intent, showStudentName)
+ } else {
+ Timber.d("Notification studentId($studentId) differs from current(${student.studentId})")
+ }
}.onEach {
if (it.status == Status.ERROR) Timber.e(it.error!!)
}.launchIn(GlobalScope)
}
- private fun prepareNotification(context: Context, intent: Intent) {
+ private fun prepareNotification(context: Context, intent: Intent, showStudentName: Boolean) {
val type = intent.getIntExtra(LESSON_TYPE, 0)
val isPersistent = preferencesRepository.isUpcomingLessonsNotificationsPersistent
@@ -78,7 +85,7 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
}
val studentId = intent.getIntExtra(STUDENT_ID, 0)
- val studentName = intent.getStringExtra(STUDENT_NAME)
+ val studentName = intent.getStringExtra(STUDENT_NAME).takeIf { showStudentName }
val subject = intent.getStringExtra(LESSON_TITLE)
val room = intent.getStringExtra(LESSON_ROOM)
@@ -91,19 +98,26 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: ${start.toLocalDateTime()}, student: $studentId")
- showNotification(
- context, isPersistent, studentName,
- if (type == NOTIFICATION_TYPE_CURRENT) end else start, end - start,
+ val notificationTitleResId =
+ if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next
+ val notificationTitle =
+ context.getString(notificationTitleResId, "($room) $subject".removePrefix("()"))
+
+ val nextLessonText = nextSubject?.let {
context.getString(
- if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next,
- "($room) $subject".removePrefix("()")
- ),
- nextSubject?.let {
- context.getString(
- R.string.timetable_later,
- "($nextRoom) $nextSubject".removePrefix("()")
- )
- }
+ R.string.timetable_later,
+ "($nextRoom) $nextSubject".removePrefix("()")
+ )
+ }
+
+ showNotification(
+ context = context,
+ isPersistent = isPersistent,
+ studentName = studentName,
+ countDown = if (type == NOTIFICATION_TYPE_CURRENT) end else start,
+ timeout = end - start,
+ title = notificationTitle,
+ next = nextLessonText
)
}
@@ -130,10 +144,11 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
.setTimeoutAfter(timeout)
.setSmallIcon(R.drawable.ic_stat_timetable)
.setColor(context.getCompatColor(R.color.colorPrimary))
- .setStyle(NotificationCompat.InboxStyle().also {
- it.setSummaryText(studentName)
- it.addLine(next)
- })
+ .setStyle(NotificationCompat.InboxStyle()
+ .addLine(next)
+ .also { inboxStyle ->
+ studentName?.let { inboxStyle.setSummaryText(it) }
+ })
.setContentIntent(
PendingIntent.getActivity(
context,
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt
index da8d094c6..6fd6c1602 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt
@@ -57,7 +57,7 @@ class AppNotificationManager @Inject constructor(
NotificationCompat.BigTextStyle()
.bigText(notificationData.content)
.also { builder ->
- if (shouldShowStudentName()) {
+ if (!studentRepository.isOneUniqueStudent()) {
builder.setSummaryText(student.nickOrName)
}
}
@@ -102,7 +102,7 @@ class AppNotificationManager @Inject constructor(
NotificationCompat.BigTextStyle()
.bigText(notificationData.content)
.also { builder ->
- if (shouldShowStudentName()) {
+ if (!studentRepository.isOneUniqueStudent()) {
builder.setSummaryText(student.nickOrName)
}
}
@@ -134,7 +134,7 @@ class AppNotificationManager @Inject constructor(
.setStyle(
NotificationCompat.InboxStyle()
.also { builder ->
- if (shouldShowStudentName()) {
+ if (!studentRepository.isOneUniqueStudent()) {
builder.setSummaryText(student.nickOrName)
}
groupNotificationData.notificationDataList.forEach {
@@ -174,7 +174,4 @@ class AppNotificationManager @Inject constructor(
notificationRepository.saveNotification(notificationEntity)
}
-
- private suspend fun shouldShowStudentName(): Boolean =
- studentRepository.getSavedStudents(decryptPass = false).size > 1
}
From 210c3a0e28d2fbbfdffcdf3b6ff55eb4ee3817ef Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Thu, 30 Dec 2021 12:50:06 +0100
Subject: [PATCH 019/669] Remove HiltBroadcastReceiver (#1736)
---
.../github/wulkanowy/services/HiltBroadcastReceiver.kt | 9 ---------
.../services/alarm/TimetableNotificationReceiver.kt | 5 ++---
.../modules/timetablewidget/TimetableWidgetProvider.kt | 5 ++---
3 files changed, 4 insertions(+), 15 deletions(-)
delete mode 100644 app/src/main/java/io/github/wulkanowy/services/HiltBroadcastReceiver.kt
diff --git a/app/src/main/java/io/github/wulkanowy/services/HiltBroadcastReceiver.kt b/app/src/main/java/io/github/wulkanowy/services/HiltBroadcastReceiver.kt
deleted file mode 100644
index 1e795d439..000000000
--- a/app/src/main/java/io/github/wulkanowy/services/HiltBroadcastReceiver.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package io.github.wulkanowy.services
-
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-
-abstract class HiltBroadcastReceiver : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {}
-}
diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
index 167060181..b4786703a 100644
--- a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
@@ -1,6 +1,7 @@
package io.github.wulkanowy.services.alarm
import android.app.PendingIntent
+import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
@@ -12,7 +13,6 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
-import io.github.wulkanowy.services.HiltBroadcastReceiver
import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity
@@ -28,7 +28,7 @@ import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
-class TimetableNotificationReceiver : HiltBroadcastReceiver() {
+class TimetableNotificationReceiver : BroadcastReceiver() {
@Inject
lateinit var studentRepository: StudentRepository
@@ -58,7 +58,6 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
@OptIn(DelicateCoroutinesApi::class)
override fun onReceive(context: Context, intent: Intent) {
- super.onReceive(context, intent)
Timber.d("Receiving intent... ${intent.toUri(0)}")
flowWithResource {
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 641d22612..07e717eaf 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
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetManager.*
+import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK
@@ -18,7 +19,6 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.data.repositories.StudentRepository
-import io.github.wulkanowy.services.HiltBroadcastReceiver
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity
@@ -33,7 +33,7 @@ import java.time.ZoneOffset
import javax.inject.Inject
@AndroidEntryPoint
-class TimetableWidgetProvider : HiltBroadcastReceiver() {
+class TimetableWidgetProvider : BroadcastReceiver() {
@Inject
lateinit var appWidgetManager: AppWidgetManager
@@ -78,7 +78,6 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
@OptIn(DelicateCoroutinesApi::class)
override fun onReceive(context: Context, intent: Intent) {
- super.onReceive(context, intent)
GlobalScope.launch {
when (intent.action) {
ACTION_APPWIDGET_UPDATE -> onUpdate(context, intent)
From ba02531aa44e6da590d1573ee6d1040d785c1c24 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 31 Dec 2021 09:40:15 +0100
Subject: [PATCH 020/669] Fix timetable widget crash when there are no lessons
for the day (#1737)
---
.../modules/timetablewidget/TimetableWidgetFactory.kt | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
index 51b790e88..411fa6625 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
@@ -73,13 +73,12 @@ class TimetableWidgetFactory(
updateTheme(appWidgetId)
lessons = getLessons(date, studentId)
- if (date == LocalDate.now()) {
- val todayLastLessonEndTimestamp =
- lessons.maxOf { it.end }.toEpochSecond(ZoneOffset.UTC)
+ val todayLastLessonEndTimestamp = lessons.maxOfOrNull { it.end }
+ if (date == LocalDate.now() && todayLastLessonEndTimestamp != null) {
sharedPref.putLong(
- getTodayLastLessonEndDateTimeWidgetKey(appWidgetId),
- todayLastLessonEndTimestamp,
- true
+ key = getTodayLastLessonEndDateTimeWidgetKey(appWidgetId),
+ value = todayLastLessonEndTimestamp.toEpochSecond(ZoneOffset.UTC),
+ sync = true
)
}
}
From bfd7f688ab099f5a6ec105b483ee528ebdbf614a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 31 Dec 2021 11:24:02 +0100
Subject: [PATCH 021/669] Move webview locale fix to account recovery fragment
(#1739)
---
.../java/io/github/wulkanowy/WulkanowyApp.kt | 22 ++-----------------
.../login/recover/LoginRecoverFragment.kt | 18 +++++++++++++++
2 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt
index 7cdeb622a..b5103e3ec 100644
--- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt
+++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt
@@ -1,10 +1,7 @@
package io.github.wulkanowy
import android.app.Application
-import android.util.Log.DEBUG
-import android.util.Log.INFO
-import android.util.Log.VERBOSE
-import android.webkit.WebView
+import android.util.Log.*
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import com.yariksoffice.lingver.Lingver
@@ -12,12 +9,7 @@ import dagger.hilt.android.HiltAndroidApp
import fr.bipi.tressence.file.FileLoggerTree
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.ui.base.ThemeManager
-import io.github.wulkanowy.utils.ActivityLifecycleLogger
-import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.AppInfo
-import io.github.wulkanowy.utils.CrashLogExceptionTree
-import io.github.wulkanowy.utils.CrashLogTree
-import io.github.wulkanowy.utils.DebugLogTree
+import io.github.wulkanowy.utils.*
import timber.log.Timber
import javax.inject.Inject
@@ -44,7 +36,6 @@ class WulkanowyApp : Application(), Configuration.Provider {
initializeAppLanguage()
themeManager.applyDefaultTheme()
initLogging()
- fixWebViewLocale()
}
private fun initLogging() {
@@ -76,15 +67,6 @@ class WulkanowyApp : Application(), Configuration.Provider {
}
}
- private fun fixWebViewLocale() {
- //https://stackoverflow.com/questions/40398528/android-webview-language-changes-abruptly-on-android-7-0-and-above
- try {
- WebView(this).destroy()
- } catch (e: Throwable) {
- //Ignore exceptions
- }
- }
-
override fun getWorkManagerConfiguration() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt
index fe32a14f1..c1c111d46 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt
@@ -11,8 +11,10 @@ import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
+import com.yariksoffice.lingver.Lingver
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.databinding.FragmentLoginRecoverBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
@@ -32,6 +34,12 @@ class LoginRecoverFragment :
@Inject
lateinit var presenter: LoginRecoverPresenter
+ @Inject
+ lateinit var lingver: Lingver
+
+ @Inject
+ lateinit var preferencesRepository: PreferencesRepository
+
companion object {
fun newInstance() = LoginRecoverFragment()
}
@@ -64,10 +72,20 @@ class LoginRecoverFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ restoreCorrectLocale()
_binding = FragmentLoginRecoverBinding.bind(view)
presenter.onAttachView(this)
}
+ // https://issuetracker.google.com/issues/37113860
+ private fun restoreCorrectLocale() {
+ if (preferencesRepository.appLanguage == "system") {
+ lingver.setFollowSystemLocale(requireContext())
+ } else {
+ lingver.setLocale(requireContext(), lingver.getLocale())
+ }
+ }
+
override fun initView() {
(requireActivity() as LoginActivity).showActionBar(true)
From e6b2acabd53753311cff09b5adb6da6e5a25517f Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Fri, 31 Dec 2021 11:53:09 +0100
Subject: [PATCH 022/669] Block app timezone to polish timezone (#1598)
---
app/build.gradle | 2 +-
.../46.json | 2430 +++++++++++++++++
.../github/wulkanowy/data/db/AppDatabase.kt | 47 +-
.../io/github/wulkanowy/data/db/Converters.kt | 21 +-
.../wulkanowy/data/db/dao/ConferenceDao.kt | 4 +-
.../wulkanowy/data/db/entities/Conference.kt | 4 +-
.../data/db/entities/GradeSummary.kt | 6 +-
.../wulkanowy/data/db/entities/Message.kt | 4 +-
.../data/db/entities/MobileDevice.kt | 4 +-
.../data/db/entities/Notification.kt | 4 +-
.../wulkanowy/data/db/entities/Student.kt | 4 +-
.../wulkanowy/data/db/entities/Timetable.kt | 6 +-
.../data/db/entities/TimetableAdditional.kt | 8 +-
.../data/db/migrations/Migration12.kt | 12 +-
.../data/db/migrations/Migration13.kt | 24 +-
.../data/db/migrations/Migration27.kt | 24 +-
.../data/db/migrations/Migration35.kt | 20 +-
.../data/db/migrations/Migration46.kt | 102 +
.../data/mappers/ConferenceMapper.kt | 2 +-
.../wulkanowy/data/mappers/MessageMapper.kt | 4 +-
.../data/mappers/MobileDeviceMapper.kt | 4 +-
.../wulkanowy/data/mappers/StudentMapper.kt | 4 +-
.../wulkanowy/data/mappers/TimetableMapper.kt | 8 +-
.../data/repositories/ConferenceRepository.kt | 12 +-
.../data/repositories/GradeRepository.kt | 20 +-
.../data/repositories/NoteRepository.kt | 6 +-
.../repositories/PreferencesRepository.kt | 27 +-
.../alarm/TimetableNotificationReceiver.kt | 3 +-
.../TimetableNotificationSchedulerHelper.kt | 22 +-
.../wulkanowy/services/sync/SyncWorker.kt | 4 +-
.../notifications/AppNotificationManager.kt | 4 +-
.../ChangeTimetableNotification.kt | 4 +-
.../NewConferenceNotification.kt | 4 +-
.../ui/modules/about/AboutFragment.kt | 11 +-
.../ui/modules/dashboard/DashboardAdapter.kt | 10 +-
.../modules/dashboard/DashboardPresenter.kt | 4 +-
.../debug/notification/mock/conference.kt | 5 +-
.../debug/notification/mock/message.kt | 4 +-
.../debug/notification/mock/timetable.kt | 7 +-
.../ui/modules/main/MainPresenter.kt | 7 +-
.../ui/modules/settings/sync/SyncPresenter.kt | 4 +-
.../ui/modules/timetable/TimetableAdapter.kt | 13 +-
.../ui/modules/timetable/TimetableDialog.kt | 4 +-
.../add/AdditionalLessonAddPresenter.kt | 10 +-
.../timetablewidget/TimetableWidgetFactory.kt | 3 +-
.../utils/MaterialDatePickerUtils.kt | 9 +-
.../io/github/wulkanowy/utils/RefreshUtils.kt | 9 +-
.../wulkanowy/utils/ResourcesExtension.kt | 7 +-
.../github/wulkanowy/utils/TimeExtension.kt | 42 +-
.../wulkanowy/utils/TimetableExtension.kt | 6 +-
.../io/github/wulkanowy/TestEnityCreator.kt | 2 +-
.../data/db/migrations/Migration13Test.kt | 34 +-
.../data/repositories/GradeRepositoryTest.kt | 78 +-
.../repositories/MessageRepositoryTest.kt | 21 +-
.../MobileDeviceRepositoryTest.kt | 5 +-
.../repositories/TimetableRepositoryTest.kt | 3 +
.../modules/grade/GradeAverageProviderTest.kt | 4 +-
.../login/form/LoginFormPresenterTest.kt | 11 +-
.../LoginStudentSelectPresenterTest.kt | 12 +-
.../wulkanowy/utils/TimeExtensionTest.kt | 40 +-
.../wulkanowy/utils/TimetableExtensionTest.kt | 51 +-
61 files changed, 2870 insertions(+), 400 deletions(-)
create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json
create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt
diff --git a/app/build.gradle b/app/build.gradle
index 135863562..4720ce4d7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -174,7 +174,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:1.4.4"
+ implementation "io.github.wulkanowy:sdk:42bce37748"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json
new file mode 100644
index 000000000..04518141c
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json
@@ -0,0 +1,2430 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 46,
+ "identityHash": "f310243440ca00cbc35e62ebaca5c7d8",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "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"
+ ],
+ "orders": [],
+ "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}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "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"
+ ],
+ "orders": [],
+ "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}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "recipient",
+ "columnName": "recipient_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "removed",
+ "columnName": "removed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+ "fields": [
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "oneDriveId",
+ "columnName": "one_drive_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "real_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ReportingUnits",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shortName",
+ "columnName": "short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderName",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roles",
+ "columnName": "roles",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceId",
+ "columnName": "device_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDismissible",
+ "columnName": "is_dismissible",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "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, 'f310243440ca00cbc35e62ebaca5c7d8')"
+ ]
+ }
+}
\ No newline at end of file
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 6b4abb40d..2f4c74fbf 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
@@ -67,49 +67,7 @@ import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.db.entities.TimetableHeader
-import io.github.wulkanowy.data.db.migrations.Migration10
-import io.github.wulkanowy.data.db.migrations.Migration11
-import io.github.wulkanowy.data.db.migrations.Migration12
-import io.github.wulkanowy.data.db.migrations.Migration13
-import io.github.wulkanowy.data.db.migrations.Migration14
-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.Migration20
-import io.github.wulkanowy.data.db.migrations.Migration21
-import io.github.wulkanowy.data.db.migrations.Migration22
-import io.github.wulkanowy.data.db.migrations.Migration23
-import io.github.wulkanowy.data.db.migrations.Migration24
-import io.github.wulkanowy.data.db.migrations.Migration25
-import io.github.wulkanowy.data.db.migrations.Migration26
-import io.github.wulkanowy.data.db.migrations.Migration27
-import io.github.wulkanowy.data.db.migrations.Migration28
-import io.github.wulkanowy.data.db.migrations.Migration29
-import io.github.wulkanowy.data.db.migrations.Migration3
-import io.github.wulkanowy.data.db.migrations.Migration30
-import io.github.wulkanowy.data.db.migrations.Migration31
-import io.github.wulkanowy.data.db.migrations.Migration32
-import io.github.wulkanowy.data.db.migrations.Migration33
-import io.github.wulkanowy.data.db.migrations.Migration34
-import io.github.wulkanowy.data.db.migrations.Migration35
-import io.github.wulkanowy.data.db.migrations.Migration36
-import io.github.wulkanowy.data.db.migrations.Migration37
-import io.github.wulkanowy.data.db.migrations.Migration38
-import io.github.wulkanowy.data.db.migrations.Migration39
-import io.github.wulkanowy.data.db.migrations.Migration4
-import io.github.wulkanowy.data.db.migrations.Migration40
-import io.github.wulkanowy.data.db.migrations.Migration41
-import io.github.wulkanowy.data.db.migrations.Migration42
-import io.github.wulkanowy.data.db.migrations.Migration43
-import io.github.wulkanowy.data.db.migrations.Migration44
-import io.github.wulkanowy.data.db.migrations.Migration5
-import io.github.wulkanowy.data.db.migrations.Migration6
-import io.github.wulkanowy.data.db.migrations.Migration7
-import io.github.wulkanowy.data.db.migrations.Migration8
-import io.github.wulkanowy.data.db.migrations.Migration9
+import io.github.wulkanowy.data.db.migrations.*
import io.github.wulkanowy.utils.AppInfo
import javax.inject.Singleton
@@ -157,7 +115,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
- const val VERSION_SCHEMA = 45
+ const val VERSION_SCHEMA = 46
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
@@ -203,6 +161,7 @@ abstract class AppDatabase : RoomDatabase() {
Migration42(),
Migration43(),
Migration44(),
+ Migration46(),
)
fun newInstance(
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt
index 1993c4338..b7013a32a 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt
@@ -1,40 +1,33 @@
package io.github.wulkanowy.data.db
import androidx.room.TypeConverter
+import io.github.wulkanowy.utils.toTimestamp
import kotlinx.serialization.SerializationException
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.time.Instant
import java.time.LocalDate
-import java.time.LocalDateTime
import java.time.Month
import java.time.ZoneOffset
-import java.util.Date
+import java.util.*
class Converters {
private val json = Json
@TypeConverter
- fun timestampToDate(value: Long?): LocalDate? = value?.run {
- Date(value).toInstant().atZone(ZoneOffset.UTC).toLocalDate()
- }
+ fun timestampToLocalDate(value: Long?): LocalDate? =
+ value?.let(::Date)?.toInstant()?.atZone(ZoneOffset.UTC)?.toLocalDate()
@TypeConverter
- fun dateToTimestamp(date: LocalDate?): Long? {
- return date?.atStartOfDay()?.toInstant(ZoneOffset.UTC)?.toEpochMilli()
- }
+ fun dateToTimestamp(date: LocalDate?): Long? = date?.toTimestamp()
@TypeConverter
- fun timestampToTime(value: Long?): LocalDateTime? = value?.let {
- LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC)
- }
+ fun instantToTimestamp(instant: Instant?): Long? = instant?.toEpochMilli()
@TypeConverter
- fun timeToTimestamp(date: LocalDateTime?): Long? {
- return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli()
- }
+ fun timestampToInstant(timestamp: Long?): Instant? = timestamp?.let(Instant::ofEpochMilli)
@TypeConverter
fun monthToInt(month: Month?) = month?.value
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt
index e84bad592..ca9da9ea9 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt
@@ -4,7 +4,7 @@ import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Conference
import kotlinx.coroutines.flow.Flow
-import java.time.LocalDateTime
+import java.time.Instant
import javax.inject.Singleton
@Dao
@@ -12,5 +12,5 @@ import javax.inject.Singleton
interface ConferenceDao : BaseDao {
@Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :startDate")
- fun loadAll(diaryId: Int, studentId: Int, startDate: LocalDateTime): Flow>
+ fun loadAll(diaryId: Int, studentId: Int, startDate: Instant): Flow>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt
index 4ad946508..ba3958dbc 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt
@@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
-import java.time.LocalDateTime
+import java.time.Instant
@Entity(tableName = "Conferences")
data class Conference(
@@ -27,7 +27,7 @@ data class Conference(
@ColumnInfo(name = "conference_id")
val conferenceId: Int,
- val date: LocalDateTime
+ val date: Instant,
) : Serializable {
@PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt
index fb7b60bbc..a42832ced 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSummary.kt
@@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
-import java.time.LocalDateTime
+import java.time.Instant
@Entity(tableName = "GradesSummary")
data class GradeSummary(
@@ -45,8 +45,8 @@ data class GradeSummary(
var isFinalGradeNotified: Boolean = true
@ColumnInfo(name = "predicted_grade_last_change")
- var predictedGradeLastChange: LocalDateTime = LocalDateTime.now()
+ var predictedGradeLastChange: Instant = Instant.now()
@ColumnInfo(name = "final_grade_last_change")
- var finalGradeLastChange: LocalDateTime = LocalDateTime.now()
+ var finalGradeLastChange: Instant = Instant.now()
}
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 7b6e0dbf2..8782bc765 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
@@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
-import java.time.LocalDateTime
+import java.time.Instant
@Entity(tableName = "Messages")
data class Message(
@@ -29,7 +29,7 @@ data class Message(
val subject: String,
- val date: LocalDateTime,
+ val date: Instant,
@ColumnInfo(name = "folder_id")
val folderId: Int,
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt
index 83d82c0b9..887e43239 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt
@@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
-import java.time.LocalDateTime
+import java.time.Instant
@Entity(tableName = "MobileDevices")
data class MobileDevice(
@@ -17,7 +17,7 @@ data class MobileDevice(
val name: String,
- val date: LocalDateTime
+ val date: Instant,
) : Serializable {
@PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt
index 740137f0f..4867e3329 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt
@@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import io.github.wulkanowy.services.sync.notifications.NotificationType
-import java.time.LocalDateTime
+import java.time.Instant
@Entity(tableName = "Notifications")
data class Notification(
@@ -18,7 +18,7 @@ data class Notification(
val type: NotificationType,
- val date: LocalDateTime,
+ val date: Instant,
val data: 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 af9fe831a..76da9643d 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
@@ -5,7 +5,7 @@ import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import java.io.Serializable
-import java.time.LocalDateTime
+import java.time.Instant
@Entity(
tableName = "Students",
@@ -74,7 +74,7 @@ data class Student(
val isCurrent: Boolean,
@ColumnInfo(name = "registration_date")
- val registrationDate: LocalDateTime
+ val registrationDate: Instant,
) : Serializable {
@PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Timetable.kt
index 29b3737bc..d23d388f9 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
@@ -4,8 +4,8 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
+import java.time.Instant
import java.time.LocalDate
-import java.time.LocalDateTime
@Entity(tableName = "Timetable")
data class Timetable(
@@ -18,9 +18,9 @@ data class Timetable(
val number: Int,
- val start: LocalDateTime,
+ val start: Instant,
- val end: LocalDateTime,
+ val end: Instant,
val date: LocalDate,
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt
index db32de874..478026102 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/TimetableAdditional.kt
@@ -4,9 +4,9 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
+import java.time.Instant
import java.time.LocalDate
-import java.time.LocalDateTime
-import java.util.UUID
+import java.util.*
@Entity(tableName = "TimetableAdditional")
data class TimetableAdditional(
@@ -17,9 +17,9 @@ data class TimetableAdditional(
@ColumnInfo(name = "diary_id")
val diaryId: Int,
- val start: LocalDateTime,
+ val start: Instant,
- val end: LocalDateTime,
+ val end: Instant,
val date: LocalDate,
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt
index 1dc38e14c..c827b82ba 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration12.kt
@@ -43,12 +43,14 @@ class Migration12 : Migration(11, 12) {
private fun getStudentsIds(database: SupportSQLiteDatabase): List {
val students = mutableListOf()
- val studentsCursor = database.query("SELECT student_id FROM Students")
- if (studentsCursor.moveToFirst()) {
- do {
- students.add(studentsCursor.getInt(0))
- } while (studentsCursor.moveToNext())
+ database.query("SELECT student_id FROM Students").use {
+ if (it.moveToFirst()) {
+ do {
+ students.add(it.getInt(0))
+ } while (it.moveToNext())
+ }
}
+
return students
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt
index 0cf8cd9b0..36de1e837 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration13.kt
@@ -25,12 +25,14 @@ class Migration13 : Migration(12, 13) {
private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList> {
val students = mutableListOf>()
- val studentsCursor = database.query("SELECT id, school_name FROM Students")
- if (studentsCursor.moveToFirst()) {
- do {
- students.add(studentsCursor.getInt(0) to studentsCursor.getString(1))
- } while (studentsCursor.moveToNext())
+ database.query("SELECT id, school_name FROM Students").use {
+ if (it.moveToFirst()) {
+ do {
+ students.add(it.getInt(0) to it.getString(1))
+ } while (it.moveToNext())
+ }
}
+
return students
}
@@ -42,12 +44,14 @@ class Migration13 : Migration(12, 13) {
private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List> {
val students = mutableListOf>()
- val studentsCursor = database.query("SELECT student_id, class_id FROM Students")
- if (studentsCursor.moveToFirst()) {
- do {
- students.add(studentsCursor.getInt(0) to studentsCursor.getInt(1))
- } while (studentsCursor.moveToNext())
+ database.query("SELECT student_id, class_id FROM Students").use {
+ if (it.moveToFirst()) {
+ do {
+ students.add(it.getInt(0) to it.getInt(1))
+ } while (it.moveToNext())
+ }
}
+
return students
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt
index 6592228a1..5c60beead 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration27.kt
@@ -22,24 +22,28 @@ class Migration27 : Migration(26, 27) {
private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList> {
val students = mutableListOf>()
- val studentsCursor = database.query("SELECT id, user_login_id, student_name FROM Students")
- if (studentsCursor.moveToFirst()) {
- do {
- students.add(Triple(studentsCursor.getLong(0), studentsCursor.getInt(1), studentsCursor.getString(2)))
- } while (studentsCursor.moveToNext())
+ database.query("SELECT id, user_login_id, student_name FROM Students").use {
+ if (it.moveToFirst()) {
+ do {
+ students.add(Triple(it.getLong(0), it.getInt(1), it.getString(2)))
+ } while (it.moveToNext())
+ }
}
+
return students
}
private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList> {
val units = mutableListOf>()
- val unitsCursor = database.query("SELECT sender_id, sender_name FROM ReportingUnits")
- if (unitsCursor.moveToFirst()) {
- do {
- units.add(unitsCursor.getInt(0) to unitsCursor.getString(1))
- } while (unitsCursor.moveToNext())
+ database.query("SELECT sender_id, sender_name FROM ReportingUnits").use {
+ if (it.moveToFirst()) {
+ do {
+ units.add(it.getInt(0) to it.getString(1))
+ } while (it.moveToNext())
+ }
}
+
return units
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt
index cc540388c..f63431d00 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration35.kt
@@ -10,15 +10,17 @@ class Migration35(private val appInfo: AppInfo) : Migration(34, 35) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0")
- val studentsCursor = database.query("SELECT * FROM Students")
-
- while (studentsCursor.moveToNext()) {
- val studentId = studentsCursor.getLongOrNull(0)
- database.execSQL(
- """UPDATE Students
- SET avatar_color = ${appInfo.defaultColorsForAvatar.random()}
- WHERE id = $studentId"""
- )
+ database.query("SELECT * FROM Students").use {
+ while (it.moveToNext()) {
+ val studentId = it.getLongOrNull(0)
+ database.execSQL(
+ """
+ UPDATE Students
+ SET avatar_color = ${appInfo.defaultColorsForAvatar.random()}
+ WHERE id = $studentId
+ """
+ )
+ }
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt
new file mode 100644
index 000000000..d3fa5cf93
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration46.kt
@@ -0,0 +1,102 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+import java.time.Instant
+import java.time.ZoneId
+import java.time.ZoneOffset
+
+class Migration46 : Migration(45, 46) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ migrateConferences(database)
+ migrateMessages(database)
+ migrateMobileDevices(database)
+ migrateNotifications(database)
+ migrateTimetable(database)
+ migrateTimetableAdditional(database)
+ }
+
+ private fun migrateConferences(database: SupportSQLiteDatabase) {
+ database.query("SELECT * FROM Conferences").use {
+ while (it.moveToNext()) {
+ val id = it.getLong(it.getColumnIndexOrThrow("id"))
+ val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
+ val timestampUtc = timestampLocal.timestampLocalToUTC()
+
+ database.execSQL("UPDATE Conferences SET date = $timestampUtc WHERE id = $id")
+ }
+ }
+ }
+
+ private fun migrateMessages(database: SupportSQLiteDatabase) {
+ database.query("SELECT * FROM Messages").use {
+ while (it.moveToNext()) {
+ val id = it.getLong(it.getColumnIndexOrThrow("id"))
+ val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
+ val timestampUtc = timestampLocal.timestampLocalToUTC()
+
+ database.execSQL("UPDATE Messages SET date = $timestampUtc WHERE id = $id")
+ }
+ }
+ }
+
+ private fun migrateMobileDevices(database: SupportSQLiteDatabase) {
+ database.query("SELECT * FROM MobileDevices").use {
+ while (it.moveToNext()) {
+ val id = it.getLong(it.getColumnIndexOrThrow("id"))
+ val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
+ val timestampUtc = timestampLocal.timestampLocalToUTC()
+
+ database.execSQL("UPDATE MobileDevices SET date = $timestampUtc WHERE id = $id")
+ }
+ }
+ }
+
+ private fun migrateNotifications(database: SupportSQLiteDatabase) {
+ database.query("SELECT * FROM Notifications").use {
+ while (it.moveToNext()) {
+ val id = it.getLong(it.getColumnIndexOrThrow("id"))
+ val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
+ val timestampUtc = timestampLocal.timestampLocalToUTC()
+
+ database.execSQL("UPDATE Notifications SET date = $timestampUtc WHERE id = $id")
+ }
+ }
+ }
+
+ private fun migrateTimetable(database: SupportSQLiteDatabase) {
+ database.query("SELECT * FROM Timetable").use {
+ while (it.moveToNext()) {
+ val id = it.getLong(it.getColumnIndexOrThrow("id"))
+ val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start"))
+ val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end"))
+ val timestampUtcStart = timestampLocalStart.timestampLocalToUTC()
+ val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC()
+
+ database.execSQL("UPDATE Timetable SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
+ }
+ }
+ }
+
+ private fun migrateTimetableAdditional(database: SupportSQLiteDatabase) {
+ database.query("SELECT * FROM TimetableAdditional").use {
+ while (it.moveToNext()) {
+ val id = it.getLong(it.getColumnIndexOrThrow("id"))
+ val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start"))
+ val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end"))
+ val timestampUtcStart = timestampLocalStart.timestampLocalToUTC()
+ val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC()
+
+ database.execSQL("UPDATE TimetableAdditional SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
+ }
+ }
+ }
+
+ private fun Long.timestampLocalToUTC(): Long = Instant.ofEpochMilli(this)
+ .atZone(ZoneOffset.UTC)
+ .withZoneSameLocal(ZoneId.of("Europe/Warsaw"))
+ .withZoneSameInstant(ZoneId.systemDefault())
+ .toInstant()
+ .toEpochMilli()
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt
index 52dc9b30b..17a9e5cdb 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/ConferenceMapper.kt
@@ -10,7 +10,7 @@ fun List.mapToEntities(semester: Semester) = map {
diaryId = semester.diaryId,
agenda = it.agenda,
conferenceId = it.id,
- date = it.date,
+ date = it.dateZoned.toInstant(),
presentOnConference = it.presentOnConference,
subject = it.subject,
title = it.title
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
index 913e4d030..13f0ab33e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
@@ -4,7 +4,7 @@ import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Student
-import java.time.LocalDateTime
+import java.time.Instant
import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
@@ -18,7 +18,7 @@ fun List.mapToEntities(student: Student) = map {
senderId = it.sender?.loginId ?: 0,
recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów",
subject = it.subject.trim(),
- date = it.date ?: LocalDateTime.now(),
+ date = it.dateZoned?.toInstant() ?: Instant.now(),
folderId = it.folderId,
unread = it.unread ?: false,
removed = it.removed,
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt
index f0c375bfa..b1e96a27b 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt
@@ -3,13 +3,13 @@ package io.github.wulkanowy.data.mappers
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.sdk.pojo.Token as SdkToken
import io.github.wulkanowy.sdk.pojo.Device as SdkDevice
+import io.github.wulkanowy.sdk.pojo.Token as SdkToken
fun List.mapToEntities(semester: Semester) = map {
MobileDevice(
userLoginId = semester.studentId,
- date = it.createDate,
+ date = it.createDateZoned.toInstant(),
deviceId = it.id,
name = it.name
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt
index c93323038..a2110d7f5 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/StudentMapper.kt
@@ -2,7 +2,7 @@ package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
-import java.time.LocalDateTime
+import java.time.Instant
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
fun List.mapToEntities(password: String = "", colors: List) = map {
@@ -24,7 +24,7 @@ fun List.mapToEntities(password: String = "", colors: List) =
scrapperBaseUrl = it.scrapperBaseUrl,
loginType = it.loginType.name,
isCurrent = false,
- registrationDate = LocalDateTime.now(),
+ registrationDate = Instant.now(),
mobileBaseUrl = it.mobileBaseUrl,
privateKey = it.privateKey,
certificateKey = it.certificateKey,
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt
index 045101c42..e55aa3cf7 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/TimetableMapper.kt
@@ -21,8 +21,8 @@ fun List.mapToEntities(semester: Semester) = map {
studentId = semester.studentId,
diaryId = semester.diaryId,
number = it.number,
- start = it.start,
- end = it.end,
+ start = it.startZoned.toInstant(),
+ end = it.endZoned.toInstant(),
date = it.date,
subject = it.subject,
subjectOld = it.subjectOld,
@@ -45,8 +45,8 @@ fun List.mapToEntities(semester: Semester) = map {
diaryId = semester.diaryId,
subject = it.subject,
date = it.date,
- start = it.start,
- end = it.end
+ start = it.startZoned.toInstant(),
+ end = it.endZoned.toInstant(),
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
index e32271833..a1667ccbc 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
@@ -6,16 +6,10 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import java.time.Instant
-import java.time.LocalDateTime
-import java.time.ZoneOffset
import javax.inject.Inject
import javax.inject.Singleton
@@ -35,7 +29,7 @@ class ConferenceRepository @Inject constructor(
semester: Semester,
forceRefresh: Boolean,
notify: Boolean = false,
- startDate: LocalDateTime = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC),
+ startDate: Instant = Instant.EPOCH,
) = networkBoundResource(
mutex = saveFetchResultMutex,
shouldFetch = {
@@ -66,7 +60,7 @@ class ConferenceRepository @Inject constructor(
conferenceDb.loadAll(
diaryId = semester.diaryId,
studentId = semester.studentId,
- startDate = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC)
+ startDate = Instant.EPOCH,
)
suspend fun updateConference(conference: List) = conferenceDb.updateAll(conference)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
index 6c574b48a..d539c14b9 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
@@ -8,16 +8,12 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
-import java.time.LocalDateTime
+import java.time.Instant
import javax.inject.Inject
import javax.inject.Singleton
@@ -70,8 +66,8 @@ class GradeRepository @Inject constructor(
newDetails: List,
notify: Boolean
) {
- val notifyBreakDate = oldGrades.maxByOrNull {it.date }
- ?.date ?: student.registrationDate.toLocalDate()
+ val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date
+ ?: student.registrationDate.toLocalDate()
gradeDb.deleteAll(oldGrades uniqueSubtract newDetails)
gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply {
@@ -101,13 +97,13 @@ class GradeRepository @Inject constructor(
}
summary.predictedGradeLastChange = when {
- oldSummary == null -> LocalDateTime.now()
- summary.predictedGrade != oldSummary.predictedGrade -> LocalDateTime.now()
+ oldSummary == null -> Instant.now()
+ summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
else -> oldSummary.predictedGradeLastChange
}
summary.finalGradeLastChange = when {
- oldSummary == null -> LocalDateTime.now()
- summary.finalGrade != oldSummary.finalGrade -> LocalDateTime.now()
+ oldSummary == null -> Instant.now()
+ summary.finalGrade != oldSummary.finalGrade -> Instant.now()
else -> oldSummary.finalGradeLastChange
}
})
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
index c1738b36e..121324d8b 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
@@ -6,11 +6,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
index e6437b167..4cd85586f 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
@@ -7,24 +7,16 @@ import com.fredporciuncula.flow.preferences.FlowSharedPreferences
import com.fredporciuncula.flow.preferences.Preference
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
-import io.github.wulkanowy.data.enums.AppTheme
-import io.github.wulkanowy.data.enums.GradeColorTheme
-import io.github.wulkanowy.data.enums.GradeExpandMode
-import io.github.wulkanowy.data.enums.GradeSortingMode
-import io.github.wulkanowy.data.enums.TimetableMode
-import io.github.wulkanowy.sdk.toLocalDate
+import io.github.wulkanowy.data.enums.*
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
-import io.github.wulkanowy.utils.toLocalDateTime
-import io.github.wulkanowy.utils.toTimestamp
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
-import java.time.LocalDate
-import java.time.LocalDateTime
+import java.time.Instant
import javax.inject.Inject
import javax.inject.Singleton
@@ -208,10 +200,10 @@ class PreferencesRepository @Inject constructor(
R.bool.pref_default_optional_arithmetic_average
)
- var lasSyncDate: LocalDateTime
+ var lasSyncDate: Instant?
get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date)
- .toLocalDateTime()
- set(value) = sharedPref.edit().putLong("last_sync_date", value.toTimestamp()).apply()
+ .takeIf { it != 0L }?.let(Instant::ofEpochMilli)
+ set(value) = sharedPref.edit().putLong("last_sync_date", value?.toEpochMilli() ?: 0).apply()
var dashboardItemsPosition: Map?
get() {
@@ -270,11 +262,12 @@ class PreferencesRepository @Inject constructor(
get() = sharedPref.getInt(PREF_KEY_IN_APP_REVIEW_COUNT, 0)
set(value) = sharedPref.edit().putInt(PREF_KEY_IN_APP_REVIEW_COUNT, value).apply()
- var inAppReviewDate: LocalDate?
+ var inAppReviewDate: Instant?
get() = sharedPref.getLong(PREF_KEY_IN_APP_REVIEW_DATE, 0).takeIf { it != 0L }
- ?.toLocalDate()
- set(value) = sharedPref.edit().putLong(PREF_KEY_IN_APP_REVIEW_DATE, value!!.toTimestamp())
- .apply()
+ ?.let(Instant::ofEpochMilli)
+ set(value) = sharedPref.edit {
+ putLong(PREF_KEY_IN_APP_REVIEW_DATE, value?.toEpochMilli() ?: 0)
+ }
var isAppReviewDone: Boolean
get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false)
diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
index b4786703a..c3ff1838e 100644
--- a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
@@ -19,7 +19,6 @@ import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.getCompatColor
-import io.github.wulkanowy.utils.toLocalDateTime
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.launchIn
@@ -95,7 +94,7 @@ class TimetableNotificationReceiver : BroadcastReceiver() {
val nextSubject = intent.getStringExtra(LESSON_NEXT_TITLE)
val nextRoom = intent.getStringExtra(LESSON_NEXT_ROOM)
- Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: ${start.toLocalDateTime()}, student: $studentId")
+ Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: $start, student: $studentId")
val notificationTitleResId =
if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next
diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt
index dc9b8f1da..42078d03f 100644
--- a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt
@@ -28,12 +28,12 @@ import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companio
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.nickOrName
-import io.github.wulkanowy.utils.toTimestamp
import kotlinx.coroutines.withContext
import timber.log.Timber
+import java.time.Duration.ofMinutes
+import java.time.Instant
+import java.time.Instant.now
import java.time.LocalDate
-import java.time.LocalDateTime
-import java.time.LocalDateTime.now
import javax.inject.Inject
class TimetableNotificationSchedulerHelper @Inject constructor(
@@ -43,14 +43,14 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
private val dispatchersProvider: DispatchersProvider,
) {
- private fun getRequestCode(time: LocalDateTime, studentId: Int) =
- (time.toTimestamp() * studentId).toInt()
+ private fun getRequestCode(time: Instant, studentId: Int): Int =
+ (time.toEpochMilli() * studentId).toInt()
private fun getUpcomingLessonTime(
index: Int,
day: List,
lesson: Timetable
- ) = day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
+ ): Instant = day.getOrNull(index - 1)?.end ?: lesson.start.minus(ofMinutes(30))
suspend fun cancelScheduled(lessons: List, student: Student) {
val studentId = student.studentId
@@ -71,7 +71,7 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
}
}
- private fun cancelScheduledTo(range: ClosedRange, requestCode: Int) {
+ private fun cancelScheduledTo(range: ClosedRange, requestCode: Int) {
if (now() in range) cancelNotification()
alarmManager.cancel(
@@ -150,8 +150,8 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
putExtra(STUDENT_ID, student.studentId)
putExtra(STUDENT_NAME, student.nickOrName)
putExtra(LESSON_ROOM, lesson.room)
- putExtra(LESSON_START, lesson.start.toTimestamp())
- putExtra(LESSON_END, lesson.end.toTimestamp())
+ putExtra(LESSON_START, lesson.start.toEpochMilli())
+ putExtra(LESSON_END, lesson.end.toEpochMilli())
putExtra(LESSON_TITLE, lesson.subject)
putExtra(LESSON_NEXT_TITLE, nextLesson?.subject)
putExtra(LESSON_NEXT_ROOM, nextLesson?.room)
@@ -162,11 +162,11 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
intent: Intent,
studentId: Int,
notificationType: Int,
- time: LocalDateTime
+ time: Instant
) {
try {
AlarmManagerCompat.setExactAndAllowWhileIdle(
- alarmManager, RTC_WAKEUP, time.toTimestamp(),
+ alarmManager, RTC_WAKEUP, time.toEpochMilli(),
PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also {
it.putExtra(LESSON_TYPE, notificationType)
}, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)
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 4a9bfd58d..5dddd9a78 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
@@ -23,7 +23,7 @@ import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.getCompatColor
import kotlinx.coroutines.withContext
import timber.log.Timber
-import java.time.LocalDateTime
+import java.time.Instant
import kotlin.random.Random
@HiltWorker
@@ -91,7 +91,7 @@ class SyncWorker @AssistedInject constructor(
}
errors.isNotEmpty() -> Result.retry()
else -> {
- preferencesRepository.lasSyncDate = LocalDateTime.now()
+ preferencesRepository.lasSyncDate = Instant.now()
Result.success()
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt
index 6fd6c1602..7ac532aeb 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt
@@ -18,7 +18,7 @@ import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.nickOrName
-import java.time.LocalDateTime
+import java.time.Instant
import javax.inject.Inject
import kotlin.random.Random
@@ -169,7 +169,7 @@ class AppNotificationManager @Inject constructor(
title = notificationData.title,
content = notificationData.content,
type = notificationType,
- date = LocalDateTime.now()
+ date = Instant.now(),
)
notificationRepository.saveNotification(notificationEntity)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt
index 7bfef96a4..b1f9a7b06 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt
@@ -11,8 +11,8 @@ import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.getPlural
import io.github.wulkanowy.utils.toFormattedString
+import java.time.Instant
import java.time.LocalDate
-import java.time.LocalDateTime
import javax.inject.Inject
class ChangeTimetableNotification @Inject constructor(
@@ -21,7 +21,7 @@ class ChangeTimetableNotification @Inject constructor(
) {
suspend fun notify(items: List, student: Student) {
- val currentTime = LocalDateTime.now()
+ val currentTime = Instant.now()
val changedLessons = items.filter { (it.canceled || it.changes) && it.start > currentTime }
val notificationDataList = changedLessons.groupBy { it.date }
.map { (date, lessons) ->
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt
index 8ef147886..d27c57285 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt
@@ -11,7 +11,7 @@ import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.getPlural
import io.github.wulkanowy.utils.toFormattedString
-import java.time.LocalDateTime
+import java.time.Instant
import javax.inject.Inject
class NewConferenceNotification @Inject constructor(
@@ -20,7 +20,7 @@ class NewConferenceNotification @Inject constructor(
) {
suspend fun notify(items: List, student: Student) {
- val today = LocalDateTime.now()
+ val today = Instant.now()
val lines = items.filter { !it.date.isBefore(today) }
.map {
"${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}"
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 1bf5c7ad9..701656b55 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
@@ -13,13 +13,8 @@ import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
import io.github.wulkanowy.ui.modules.debug.DebugFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
-import io.github.wulkanowy.utils.AppInfo
-import io.github.wulkanowy.utils.getCompatDrawable
-import io.github.wulkanowy.utils.openAppInMarket
-import io.github.wulkanowy.utils.openEmailClient
-import io.github.wulkanowy.utils.openInternetBrowser
-import io.github.wulkanowy.utils.toFormattedString
-import io.github.wulkanowy.utils.toLocalDateTime
+import io.github.wulkanowy.utils.*
+import java.time.Instant
import javax.inject.Inject
@AndroidEntryPoint
@@ -38,7 +33,7 @@ class AboutFragment : BaseFragment(R.layout.fragment_about
override val versionRes: Triple?
get() = context?.run {
val buildTimestamp =
- appInfo.buildTimestamp.toLocalDateTime().toFormattedString("yyyy-MM-dd")
+ Instant.ofEpochMilli(appInfo.buildTimestamp).toFormattedString("yyyy-MM-dd")
val versionSignature =
"${appInfo.versionName}-${appInfo.buildFlavor} (${appInfo.versionCode}), $buildTimestamp"
Triple(
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt
index 12be144cd..3b6dc7298 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt
@@ -37,9 +37,7 @@ import io.github.wulkanowy.utils.left
import io.github.wulkanowy.utils.nickOrName
import io.github.wulkanowy.utils.toFormattedString
import timber.log.Timber
-import java.time.Duration
-import java.time.LocalDate
-import java.time.LocalDateTime
+import java.time.*
import java.util.Timer
import javax.inject.Inject
import kotlin.concurrent.timer
@@ -291,7 +289,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter= 50 &&
- LocalDate.now().minusDays(14).isAfter(prefRepository.inAppReviewDate)
+ Instant.now().minus(Duration.ofDays(14)).isAfter(prefRepository.inAppReviewDate)
) {
view?.showInAppReview()
prefRepository.isAppReviewDone = true
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt
index fc47e29ab..1ecb4a6ef 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncPresenter.kt
@@ -79,9 +79,7 @@ class SyncPresenter @Inject constructor(
}
private fun setSyncDateInView() {
- val lastSyncDate = preferencesRepository.lasSyncDate
-
- if (lastSyncDate.year == 1970) return
+ val lastSyncDate = preferencesRepository.lasSyncDate ?: return
view?.setLastSyncDate(lastSyncDate.toFormattedString("dd.MM.yyyy HH:mm:ss"))
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
index 422cc50e8..eacd12c69 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
@@ -15,15 +15,10 @@ import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.enums.TimetableMode
import io.github.wulkanowy.databinding.ItemTimetableBinding
import io.github.wulkanowy.databinding.ItemTimetableSmallBinding
-import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.isJustFinished
-import io.github.wulkanowy.utils.isShowTimeUntil
-import io.github.wulkanowy.utils.left
-import io.github.wulkanowy.utils.toFormattedString
-import io.github.wulkanowy.utils.until
+import io.github.wulkanowy.utils.*
import timber.log.Timber
-import java.time.LocalDateTime
-import java.util.Timer
+import java.time.Instant
+import java.util.*
import javax.inject.Inject
import kotlin.concurrent.timer
@@ -167,7 +162,7 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter i < position && !item.isStudentPlan }.size)
?.let {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt
index 5bbaaa60f..c9243b12e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableDialog.kt
@@ -16,7 +16,7 @@ import io.github.wulkanowy.utils.capitalise
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.lifecycleAwareVariable
import io.github.wulkanowy.utils.toFormattedString
-import java.time.LocalDateTime
+import java.time.Instant
class TimetableDialog : DialogFragment() {
@@ -192,7 +192,7 @@ class TimetableDialog : DialogFragment() {
}
@SuppressLint("SetTextI18n")
- private fun setTime(start: LocalDateTime, end: LocalDateTime) {
+ private fun setTime(start: Instant, end: Instant) {
binding.timetableDialogTimeValue.text =
"${start.toFormattedString("HH:mm")} - ${end.toFormattedString("HH:mm")}"
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt
index 7bed5619e..c207165d3 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/add/AdditionalLessonAddPresenter.kt
@@ -10,11 +10,9 @@ import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
import io.github.wulkanowy.utils.toLocalDate
import kotlinx.coroutines.launch
import timber.log.Timber
-import java.time.LocalDate
-import java.time.LocalDateTime
-import java.time.LocalTime
+import java.time.*
import java.time.temporal.ChronoUnit
-import java.util.UUID
+import java.util.*
import javax.inject.Inject
class AdditionalLessonAddPresenter @Inject constructor(
@@ -138,8 +136,8 @@ class AdditionalLessonAddPresenter @Inject constructor(
TimetableAdditional(
studentId = semester.studentId,
diaryId = semester.diaryId,
- start = LocalDateTime.of(date, start),
- end = LocalDateTime.of(date, end),
+ start = ZonedDateTime.of(date, start, ZoneId.systemDefault()).toInstant(),
+ end = ZonedDateTime.of(date, end, ZoneId.systemDefault()).toInstant(),
date = date.plusWeeks(it),
subject = subject
).apply {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
index 411fa6625..18eefc5da 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
@@ -29,7 +29,6 @@ import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.runBlocking
import timber.log.Timber
import java.time.LocalDate
-import java.time.ZoneOffset
class TimetableWidgetFactory(
private val timetableRepository: TimetableRepository,
@@ -77,7 +76,7 @@ class TimetableWidgetFactory(
if (date == LocalDate.now() && todayLastLessonEndTimestamp != null) {
sharedPref.putLong(
key = getTodayLastLessonEndDateTimeWidgetKey(appWidgetId),
- value = todayLastLessonEndTimestamp.toEpochSecond(ZoneOffset.UTC),
+ value = todayLastLessonEndTimestamp.epochSecond,
sync = true
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt
index 14b5989b0..09ccda899 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/MaterialDatePickerUtils.kt
@@ -5,7 +5,6 @@ import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.MaterialDatePicker
import kotlinx.parcelize.Parcelize
import java.time.LocalDate
-import java.time.ZoneOffset
import java.time.temporal.ChronoUnit
fun Fragment.openMaterialDatePicker(
@@ -16,17 +15,17 @@ fun Fragment.openMaterialDatePicker(
) {
val constraintsBuilder = CalendarConstraints.Builder().apply {
setValidator(CalendarDayRangeValidator(rangeStart, rangeEnd))
- setStart(rangeStart.toTimestamp(ZoneOffset.UTC))
- setEnd(rangeEnd.toTimestamp(ZoneOffset.UTC))
+ setStart(rangeStart.toTimestamp())
+ setEnd(rangeEnd.toTimestamp())
}
val datePicker = MaterialDatePicker.Builder.datePicker()
.setCalendarConstraints(constraintsBuilder.build())
- .setSelection(selected.toTimestamp(ZoneOffset.UTC))
+ .setSelection(selected.toTimestamp())
.build()
datePicker.addOnPositiveButtonClickListener {
- val date = it.toLocalDateTime(ZoneOffset.UTC).toLocalDate()
+ val date = it.toLocalDateTime().toLocalDate()
onDateSelected(date)
}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt
index 6bf97bae7..c69fec65c 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt
@@ -8,8 +8,9 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder
import timber.log.Timber
+import java.time.Duration.ofMinutes
+import java.time.Instant
import java.time.LocalDate
-import java.time.LocalDateTime
import javax.inject.Inject
fun getRefreshKey(name: String, semester: Semester, start: LocalDate, end: LocalDate): String {
@@ -34,10 +35,10 @@ class AutoRefreshHelper @Inject constructor(
) {
fun shouldBeRefreshed(key: String): Boolean {
- val timestamp = sharedPref.getLong(key, 0).toLocalDateTime()
+ val timestamp = sharedPref.getLong(key, 0).let(Instant::ofEpochMilli)
val servicesInterval = sharedPref.getString(context.getString(R.string.pref_key_services_interval), context.getString(R.string.pref_default_services_interval)).toLong()
- val shouldBeRefreshed = timestamp < LocalDateTime.now().minusMinutes(servicesInterval)
+ val shouldBeRefreshed = timestamp < Instant.now().minus(ofMinutes(servicesInterval))
Timber.d("Check if $key need to be refreshed: $shouldBeRefreshed (last refresh: $timestamp, interval: $servicesInterval min)")
@@ -45,6 +46,6 @@ class AutoRefreshHelper @Inject constructor(
}
fun updateLastRefreshTimestamp(key: String) {
- sharedPref.putLong(key, LocalDateTime.now().toTimestamp())
+ sharedPref.putLong(key, Instant.now().toEpochMilli())
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt
index da5fd3dbb..71d3fd173 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt
@@ -12,12 +12,17 @@ import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
import okhttp3.internal.http2.StreamResetException
import java.io.InterruptedIOException
import java.net.ConnectException
+import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
fun Resources.getString(error: Throwable) = when (error) {
is UnknownHostException -> getString(R.string.error_no_internet)
- is SocketTimeoutException, is InterruptedIOException, is ConnectException, is StreamResetException -> getString(R.string.error_timeout)
+ is SocketException,
+ is SocketTimeoutException,
+ is InterruptedIOException,
+ is ConnectException,
+ is StreamResetException -> getString(R.string.error_timeout)
is NotLoggedInException -> getString(R.string.error_login_failed)
is PasswordChangeRequiredException -> getString(R.string.error_password_change_required)
is ServiceUnavailableException -> getString(R.string.error_service_unavailable)
diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt
index 355f3ab49..e7a50d0c3 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt
@@ -1,40 +1,34 @@
package io.github.wulkanowy.utils
import java.text.SimpleDateFormat
-import java.time.DayOfWeek.FRIDAY
-import java.time.DayOfWeek.MONDAY
-import java.time.DayOfWeek.SATURDAY
-import java.time.DayOfWeek.SUNDAY
-import java.time.Instant
-import java.time.LocalDate
-import java.time.LocalDateTime
-import java.time.Month
-import java.time.ZoneId
-import java.time.ZoneOffset
+import java.time.*
+import java.time.DayOfWeek.*
import java.time.format.DateTimeFormatter
-import java.time.temporal.TemporalAdjusters.firstInMonth
-import java.time.temporal.TemporalAdjusters.next
-import java.time.temporal.TemporalAdjusters.previous
-import java.util.Locale
+import java.time.temporal.TemporalAdjusters.*
+import java.util.*
private const val DEFAULT_DATE_PATTERN = "dd.MM.yyyy"
+fun LocalDate.toTimestamp(): Long = atStartOfDay()
+ .toInstant(ZoneOffset.UTC)
+ .toEpochMilli()
+
+fun Long.toLocalDateTime(): LocalDateTime = LocalDateTime.ofInstant(
+ Instant.ofEpochMilli(this), ZoneOffset.UTC
+)
+
+fun Instant.toLocalDate(): LocalDate = atZone(ZoneOffset.UTC).toLocalDate()
+
fun String.toLocalDate(format: String = DEFAULT_DATE_PATTERN): LocalDate =
LocalDate.parse(this, DateTimeFormatter.ofPattern(format))
-fun LocalDateTime.toTimestamp(tz: ZoneId = ZoneId.systemDefault()) =
- atZone(tz).withZoneSameInstant(ZoneOffset.UTC).toInstant().toEpochMilli()
-
-fun Long.toLocalDateTime(tz: ZoneId = ZoneId.systemDefault()): LocalDateTime =
- LocalDateTime.ofInstant(Instant.ofEpochMilli(this), tz)
-
-fun LocalDate.toTimestamp(tz: ZoneId = ZoneId.systemDefault()) = atStartOfDay().toTimestamp(tz)
-
fun LocalDate.toFormattedString(pattern: String = DEFAULT_DATE_PATTERN): String =
format(DateTimeFormatter.ofPattern(pattern))
-fun LocalDateTime.toFormattedString(pattern: String = DEFAULT_DATE_PATTERN): String =
- format(DateTimeFormatter.ofPattern(pattern))
+fun Instant.toFormattedString(
+ pattern: String = DEFAULT_DATE_PATTERN,
+ tz: ZoneId = ZoneId.systemDefault()
+): String = atZone(tz).format(DateTimeFormatter.ofPattern(pattern))
fun Month.getFormattedName(): String {
val formatter = SimpleDateFormat("LLLL", Locale.getDefault())
diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt
index 9d15216c6..3e94463b9 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/TimetableExtension.kt
@@ -3,10 +3,10 @@ package io.github.wulkanowy.utils
import io.github.wulkanowy.data.db.entities.Timetable
import java.time.Duration
import java.time.Duration.between
-import java.time.LocalDateTime
-import java.time.LocalDateTime.now
+import java.time.Instant
+import java.time.Instant.now
-fun Timetable.isShowTimeUntil(previousLessonEnd: LocalDateTime?) = when {
+fun Timetable.isShowTimeUntil(previousLessonEnd: Instant?) = when {
!isStudentPlan -> false
canceled -> false
now().isAfter(start) -> false
diff --git a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
index a0fa209fd..89ccade1b 100644
--- a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
+++ b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
@@ -4,7 +4,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import java.time.LocalDate
-import java.time.LocalDateTime.now
+import java.time.Instant.now
import io.github.wulkanowy.sdk.pojo.Semester as SdkSemester
fun getSemesterEntity(diaryId: Int = 1, semesterId: Int = 1, start: LocalDate = LocalDate.now(), end: LocalDate = LocalDate.now(), semesterName: Int = 1) = Semester(
diff --git a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
index 2350da459..261a6c1cf 100644
--- a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
@@ -153,23 +153,25 @@ class Migration13Test : AbstractMigrationTest() {
private fun getSemesters(db: SupportSQLiteDatabase, query: String): List> {
val semesters = mutableListOf>()
- val cursor = db.query(query)
- if (cursor.moveToFirst()) {
- do {
- semesters.add(Semester(
- studentId = cursor.getInt(1),
- diaryId = cursor.getInt(2),
- diaryName = cursor.getString(3),
- semesterId = cursor.getInt(4),
- semesterName = cursor.getInt(5),
- classId = cursor.getInt(7),
- unitId = cursor.getInt(8),
- schoolYear = cursor.getInt(9),
- start = Converters().timestampToDate(cursor.getLong(10))!!,
- end = Converters().timestampToDate(cursor.getLong(11))!!
- ) to (cursor.getInt(6) == 1))
- } while (cursor.moveToNext())
+ db.query(query).use {
+ if (it.moveToFirst()) {
+ do {
+ semesters.add(Semester(
+ studentId = it.getInt(1),
+ diaryId = it.getInt(2),
+ diaryName = it.getString(3),
+ semesterId = it.getInt(4),
+ semesterName = it.getInt(5),
+ classId = it.getInt(7),
+ unitId = it.getInt(8),
+ schoolYear = it.getInt(9),
+ start = Converters().timestampToLocalDate(it.getLong(10))!!,
+ end = Converters().timestampToLocalDate(it.getLong(11))!!
+ ) to (it.getInt(6) == 1))
+ } while (it.moveToNext())
+ }
}
+
return semesters.toList()
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
index 6dd30a579..f7968bc41 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
@@ -8,23 +8,17 @@ import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.toFirstResult
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerify
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
-import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
+import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import java.time.LocalDate
import java.time.LocalDate.of
+import java.time.ZoneOffset
import io.github.wulkanowy.sdk.pojo.Grade as SdkGrade
class GradeRepositoryTest {
@@ -57,7 +51,11 @@ class GradeRepositoryTest {
coEvery { gradeDb.deleteAll(any()) } just Runs
coEvery { gradeDb.insertAll(any()) } returns listOf()
- coEvery { gradeSummaryDb.loadAll(1, 1) } returnsMany listOf(flowOf(listOf()), flowOf(listOf()), flowOf(listOf()))
+ coEvery { gradeSummaryDb.loadAll(1, 1) } returnsMany listOf(
+ flowOf(listOf()),
+ flowOf(listOf()),
+ flowOf(listOf())
+ )
coEvery { gradeSummaryDb.deleteAll(any()) } just Runs
coEvery { gradeSummaryDb.insertAll(any()) } returns listOf()
}
@@ -65,7 +63,7 @@ class GradeRepositoryTest {
@Test
fun `mark grades older than registration date as read`() {
// prepare
- val boundaryDate = of(2019, 2, 27).atStartOfDay()
+ val boundaryDate = of(2019, 2, 27).atStartOfDay().toInstant(ZoneOffset.UTC)
val remoteList = 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"),
@@ -81,7 +79,13 @@ class GradeRepositoryTest {
)
// execute
- val res = runBlocking { gradeRepository.getGrades(student.copy(registrationDate = boundaryDate), semester, true).toFirstResult() }
+ val res = runBlocking {
+ gradeRepository.getGrades(
+ student = student.copy(registrationDate = boundaryDate),
+ semester = semester,
+ forceRefresh = true
+ ).toFirstResult()
+ }
// verify
assertEquals(null, res.error)
@@ -101,9 +105,19 @@ class GradeRepositoryTest {
fun `mitigate mark grades as unread when old grades changed`() {
// prepare
val remoteList = listOf(
- createGradeApi(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"),
+ 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"),
+ createGradeApi(
+ 3,
+ 4.0,
+ of(2019, 2, 27),
+ "Ta jest z tego samego dnia co ostatnia lokalnie"
+ ),
createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa")
)
coEvery { sdk.getGrades(1) } returns (remoteList to emptyList())
@@ -111,7 +125,12 @@ class GradeRepositoryTest {
val localList = listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Jedna ocena"),
createGradeApi(4, 4.0, of(2019, 2, 26), "Druga"),
- createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie")
+ createGradeApi(
+ 3,
+ 4.0,
+ of(2019, 2, 27),
+ "Ta jest z tego samego dnia co ostatnia lokalnie"
+ )
)
coEvery { gradeDb.loadAll(1, 1) } returnsMany listOf(
flowOf(localList.mapToEntities(semester)),
@@ -248,18 +267,19 @@ class GradeRepositoryTest {
assertEquals(0, res.data?.first?.size)
}
- private fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String) = SdkGrade(
- subject = "",
- color = "",
- comment = "",
- date = date,
- description = desc,
- entry = "",
- modifier = .0,
- symbol = "",
- teacher = "",
- value = value.toDouble(),
- weight = weight.toString(),
- weightValue = weight
- )
+ private fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String) =
+ SdkGrade(
+ 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/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
index 052f08f00..f21fc1780 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
@@ -16,27 +16,25 @@ import io.github.wulkanowy.sdk.pojo.MessageDetails
import io.github.wulkanowy.sdk.pojo.Sender
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.toFirstResult
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.checkEquals
-import io.mockk.coEvery
-import io.mockk.coVerify
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
-import io.mockk.just
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import java.net.UnknownHostException
-import java.time.LocalDateTime
+import java.time.Instant
+import java.time.ZoneOffset
import kotlin.test.assertTrue
+@OptIn(ExperimentalCoroutinesApi::class)
class MessageRepositoryTest {
@SpyK
@@ -80,7 +78,7 @@ class MessageRepositoryTest {
}
@Test
- fun `get messages when read by values was changed on already read message`() = runBlocking {
+ fun `get messages when read by values was changed on already read message`() = runTest {
every { messageDb.loadAll(any(), any()) } returns flow {
val dbMessage = getMessageEntity(3, "", false).apply {
unreadBy = 10
@@ -239,7 +237,7 @@ class MessageRepositoryTest {
senderId = 0,
recipient = "Wielu adresatów",
subject = "",
- date = LocalDateTime.MAX,
+ date = Instant.EPOCH,
folderId = 1,
unread = unread,
removed = false,
@@ -261,7 +259,8 @@ class MessageRepositoryTest {
recipients = listOf(),
subject = "",
content = content,
- date = LocalDateTime.MAX,
+ date = Instant.EPOCH.atZone(ZoneOffset.UTC).toLocalDateTime(),
+ dateZoned = Instant.EPOCH.atZone(ZoneOffset.UTC),
folderId = 1,
unread = unread,
unreadBy = 0,
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
index 52a076d3c..c5a7756ff 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
@@ -22,6 +22,7 @@ import org.junit.Assert
import org.junit.Before
import org.junit.Test
import java.time.LocalDateTime.of
+import java.time.ZoneId
class MobileDeviceRepositoryTest {
@@ -137,6 +138,8 @@ class MobileDeviceRepositoryTest {
name = "",
deviceId = "",
createDate = of(2019, 5, day, 0, 0, 0),
- modificationDate = of(2019, 5, day, 0, 0, 0)
+ modificationDate = of(2019, 5, day, 0, 0, 0),
+ createDateZoned = of(2019, 5, day, 0, 0, 0).atZone(ZoneId.systemDefault()),
+ modificationDateZoned = of(2019, 5, day, 0, 0, 0).atZone(ZoneId.systemDefault())
)
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
index edb3125eb..adb4f33a1 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
@@ -27,6 +27,7 @@ import org.junit.Test
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalDateTime.of
+import java.time.ZoneId
import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable
class TimetableRepositoryTest {
@@ -107,6 +108,8 @@ class TimetableRepositoryTest {
number = number,
start = start,
end = start.plusMinutes(45),
+ startZoned = start.atZone(ZoneId.systemDefault()),
+ endZoned = start.plusMinutes(45).atZone(ZoneId.systemDefault()),
date = start.toLocalDate(),
subject = subject,
group = "",
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 ec07e149f..f097cb845 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
@@ -23,9 +23,9 @@ import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
+import java.time.Instant
import java.time.LocalDate.now
import java.time.LocalDate.of
-import java.time.LocalDateTime
class GradeAverageProviderTest {
@@ -63,7 +63,7 @@ class GradeAverageProviderTest {
className = "",
classId = 1,
isCurrent = true,
- registrationDate = LocalDateTime.now()
+ registrationDate = Instant.now()
)
private val semesters = mutableListOf(
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 67d3e6266..9bcfb8b6c 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
@@ -6,18 +6,13 @@ import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
-import io.mockk.just
-import io.mockk.verify
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import java.io.IOException
-import java.time.LocalDateTime.now
+import java.time.Instant
class LoginFormPresenterTest {
@@ -121,7 +116,7 @@ class LoginFormPresenterTest {
classId = 1,
isCurrent = false,
symbol = "",
- registrationDate = now(),
+ registrationDate = Instant.now(),
className = "",
mobileBaseUrl = "",
privateKey = "",
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 1ec885904..e52ec3ae2 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
@@ -7,18 +7,12 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.clearMocks
-import io.mockk.coEvery
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
-import io.mockk.just
-import io.mockk.verify
import org.junit.Before
import org.junit.Rule
import org.junit.Test
-import java.time.LocalDateTime.now
+import java.time.Instant
class LoginStudentSelectPresenterTest {
@@ -55,7 +49,7 @@ class LoginStudentSelectPresenterTest {
schoolSymbol = "",
classId = 1,
studentName = "",
- registrationDate = now(),
+ registrationDate = Instant.now(),
className = "",
loginMode = "",
certificateKey = "",
diff --git a/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt b/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt
index 9a3bf9fea..70ad6d534 100644
--- a/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt
@@ -1,14 +1,13 @@
package io.github.wulkanowy.utils
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
+import org.junit.Assert.*
import org.junit.Test
+import java.time.Instant
import java.time.LocalDate.of
import java.time.LocalDateTime
import java.time.Month.JANUARY
import java.time.ZoneOffset
-import java.util.Locale
+import java.util.*
class TimeExtensionTest {
@@ -24,11 +23,15 @@ class TimeExtensionTest {
}
@Test
- fun toFormattedStringLocalDateTimeTest() {
- assertEquals("01.10.2018", LocalDateTime.of(2018, 10, 1, 10, 0, 0).toFormattedString())
+ fun toFormattedStringFromInstantTest() {
+ assertEquals(
+ "01.10.2018",
+ LocalDateTime.of(2018, 10, 1, 10, 0, 0).toInstant(ZoneOffset.UTC).toFormattedString()
+ )
assertEquals(
"2018-10-01 10:00:00",
- LocalDateTime.of(2018, 10, 1, 10, 0, 0).toFormattedString("uuuu-MM-dd HH:mm:ss")
+ LocalDateTime.of(2018, 10, 1, 10, 0, 0).toInstant(ZoneOffset.UTC)
+ .toFormattedString("uuuu-MM-dd HH:mm:ss", ZoneOffset.UTC)
)
}
@@ -228,16 +231,23 @@ class TimeExtensionTest {
}
@Test
- fun getLocalDateToTimestampUTC() {
- assertEquals(0L, of(1970, 1, 1).toTimestamp(ZoneOffset.UTC))
- assertEquals(946684800000L, of(2000, 1, 1).toTimestamp(ZoneOffset.UTC))
- assertEquals(1640131200000L, of(2021, 12, 22).toTimestamp(ZoneOffset.UTC))
+ fun getLocalDateToTimestamp() {
+ assertEquals(0L, of(1970, 1, 1).toTimestamp())
+ assertEquals(946684800000L, of(2000, 1, 1).toTimestamp())
+ assertEquals(1640131200000L, of(2021, 12, 22).toTimestamp())
}
@Test
- fun getLocalDateTimeToUtcTimestamp() {
- assertEquals(0L, LocalDateTime.of(1970, 1, 1, 0, 0, 0).toTimestamp(ZoneOffset.UTC))
- assertEquals(946684800000L, LocalDateTime.of(2000, 1, 1, 0, 0, 0).toTimestamp(ZoneOffset.UTC))
- assertEquals(1640131200000L, LocalDateTime.of(2021, 12, 22, 0, 0, 0).toTimestamp(ZoneOffset.UTC))
+ fun getLocalDateFromInstant() {
+ assertEquals(of(1970, 1, 1), Instant.ofEpochMilli(0).toLocalDate())
+ assertEquals(of(2000, 1, 1), Instant.ofEpochMilli(946684800000).toLocalDate())
+ assertEquals(of(2021, 12, 22), Instant.ofEpochMilli(1640131200000L).toLocalDate())
+ }
+
+ @Test
+ fun timestampToLocalDateTime() {
+ assertEquals(LocalDateTime.of(1970, 1, 1, 0, 0, 0), 0L.toLocalDateTime())
+ assertEquals(LocalDateTime.of(2000, 1, 1, 0, 0, 0), 946684800000.toLocalDateTime())
+ assertEquals(LocalDateTime.of(2021, 12, 22, 0, 0, 0), 1640131200000L.toLocalDateTime())
}
}
diff --git a/app/src/test/java/io/github/wulkanowy/utils/TimetableExtensionTest.kt b/app/src/test/java/io/github/wulkanowy/utils/TimetableExtensionTest.kt
index b7fa58c6f..a4d649e26 100644
--- a/app/src/test/java/io/github/wulkanowy/utils/TimetableExtensionTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/utils/TimetableExtensionTest.kt
@@ -6,9 +6,8 @@ import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
import org.junit.Test
-import java.time.LocalDate
-import java.time.LocalDateTime
-import java.time.LocalDateTime.now
+import java.time.*
+import java.time.Duration.ofMinutes
class TimetableExtensionTest {
@@ -17,52 +16,38 @@ class TimetableExtensionTest {
assertFalse(getTimetableEntity().isShowTimeUntil(null))
assertFalse(getTimetableEntity(isStudentPlan = false).isShowTimeUntil(null))
assertFalse(getTimetableEntity(isStudentPlan = true, canceled = true).isShowTimeUntil(null))
- assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().minusSeconds(1)).isShowTimeUntil(null))
- assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().plusMinutes(5)).isShowTimeUntil(now().plusMinutes(5)))
- assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().plusMinutes(61)).isShowTimeUntil(now().minusMinutes(5)))
+ assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().minusSeconds(1)).isShowTimeUntil(null))
+ assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().plus(ofMinutes(5))).isShowTimeUntil(Instant.now().plus(ofMinutes(5))))
+ assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().plus(ofMinutes(61))).isShowTimeUntil(Instant.now().minus(ofMinutes(5))))
- assertTrue(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().plusMinutes(60)).isShowTimeUntil(now().minusMinutes(5)))
- assertTrue(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().plusMinutes(60)).isShowTimeUntil(null))
+ assertTrue(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().plus(ofMinutes(60))).isShowTimeUntil(Instant.now().minus(ofMinutes(5))))
+ assertTrue(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().plus(ofMinutes(60))).isShowTimeUntil(null))
- assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().minusSeconds(1)).isShowTimeUntil(null))
+ assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().minusSeconds(1)).isShowTimeUntil(null))
}
@Test
fun getLeft() {
assertEquals(null, getTimetableEntity(canceled = true).left)
- assertEquals(null, getTimetableEntity(start = now().plusMinutes(5), end = now().plusMinutes(50)).left)
- assertEquals(null, getTimetableEntity(start = now().minusMinutes(1), end = now().plusMinutes(44), isStudentPlan = false).left)
- assertNotEquals(
- null,
- getTimetableEntity(
- start = now().minusMinutes(1),
- end = now().plusMinutes(44),
- isStudentPlan = true
- ).left
- )
- assertNotEquals(
- null,
- getTimetableEntity(
- start = now(),
- end = now().plusMinutes(45),
- isStudentPlan = true
- ).left
- )
+ assertEquals(null, getTimetableEntity(start = Instant.now().plus(ofMinutes(5)), end = Instant.now().plus(ofMinutes(50))).left)
+ assertEquals(null, getTimetableEntity(start = Instant.now().minus(ofMinutes(1)), end = Instant.now().plus(ofMinutes(44)), isStudentPlan = false).left)
+ assertNotEquals(null, getTimetableEntity(start = Instant.now().minus(ofMinutes(1)), end = Instant.now().plus(ofMinutes(44)), isStudentPlan = true).left)
+ assertNotEquals(null, getTimetableEntity(start = Instant.now(), end = Instant.now().plus(ofMinutes(45)), isStudentPlan = true).left)
}
@Test
fun isJustFinished() {
- assertFalse(getTimetableEntity(end = now().minusSeconds(16)).isJustFinished)
- assertTrue(getTimetableEntity(end = now().minusSeconds(14)).isJustFinished)
- assertTrue(getTimetableEntity(end = now().minusSeconds(1)).isJustFinished)
- assertFalse(getTimetableEntity(end = now().plusSeconds(1)).isJustFinished)
+ assertFalse(getTimetableEntity(end = Instant.now().minusSeconds(16)).isJustFinished)
+ assertTrue(getTimetableEntity(end = Instant.now().minusSeconds(14)).isJustFinished)
+ assertTrue(getTimetableEntity(end = Instant.now().minusSeconds(1)).isJustFinished)
+ assertFalse(getTimetableEntity(end = Instant.now().plusSeconds(1)).isJustFinished)
}
private fun getTimetableEntity(
isStudentPlan: Boolean = false,
canceled: Boolean = false,
- start: LocalDateTime = now(),
- end: LocalDateTime = now()
+ start: Instant = Instant.now(),
+ end: Instant = Instant.now()
) = Timetable(
studentId = 0,
subject = "",
From 5321d00ee92b102572b90df06a46055417e110af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Fri, 31 Dec 2021 12:10:56 +0100
Subject: [PATCH 023/669] Fix play flavor build (#1740)
---
.../wulkanowy/services/messaging/AppMessagingService.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/play/java/io/github/wulkanowy/services/messaging/AppMessagingService.kt b/app/src/play/java/io/github/wulkanowy/services/messaging/AppMessagingService.kt
index 6e5c7e89c..d9a3780ba 100644
--- a/app/src/play/java/io/github/wulkanowy/services/messaging/AppMessagingService.kt
+++ b/app/src/play/java/io/github/wulkanowy/services/messaging/AppMessagingService.kt
@@ -12,7 +12,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import timber.log.Timber
-import java.time.LocalDateTime
+import java.time.Instant
import javax.inject.Inject
@SuppressLint("MissingFirebaseInstanceTokenRefresh")
@@ -36,7 +36,7 @@ class AppMessagingService : FirebaseMessagingService() {
title = title,
content = content,
data = customData,
- date = LocalDateTime.now(),
+ date = Instant.now(),
type = NotificationType.PUSH,
studentId = -1
)
From 20673c4eaddefdd3612314a52ff5376544d4b796 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 31 Dec 2021 12:21:52 +0100
Subject: [PATCH 024/669] Add basic support for kindergarten students (#1738)
---
app/build.gradle | 2 +-
.../47.json | 2438 +++++++++++++++++
.../github/wulkanowy/data/db/AppDatabase.kt | 5 +-
.../wulkanowy/data/db/entities/Semester.kt | 12 +-
.../wulkanowy/data/mappers/SemesterMapper.kt | 1 +
.../data/repositories/AttendanceRepository.kt | 14 +-
.../AttendanceSummaryRepository.kt | 9 +-
.../CompletedLessonsRepository.kt | 11 +-
.../data/repositories/ConferenceRepository.kt | 3 +-
.../data/repositories/ExamRepository.kt | 11 +-
.../data/repositories/GradeRepository.kt | 2 +-
.../repositories/GradeStatisticsRepository.kt | 17 +-
.../data/repositories/HomeworkRepository.kt | 11 +-
.../repositories/MobileDeviceRepository.kt | 15 +-
.../data/repositories/NoteRepository.kt | 3 +-
.../data/repositories/SchoolRepository.kt | 4 +-
.../data/repositories/SemesterRepository.kt | 18 +-
.../repositories/StudentInfoRepository.kt | 3 +-
.../data/repositories/SubjectRepository.kt | 9 +-
.../data/repositories/TeacherRepository.kt | 9 +-
.../data/repositories/TimetableRepository.kt | 16 +-
.../io/github/wulkanowy/TestEnityCreator.kt | 2 +
.../data/db/migrations/Migration13Test.kt | 1 +
23 files changed, 2516 insertions(+), 100 deletions(-)
create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json
diff --git a/app/build.gradle b/app/build.gradle
index 4720ce4d7..b7a075b98 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -174,7 +174,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:42bce37748"
+ implementation "io.github.wulkanowy:sdk:f6f32b755a"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json
new file mode 100644
index 000000000..3f8291eac
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json
@@ -0,0 +1,2438 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 47,
+ "identityHash": "ac88c80d4bb923b22f22ce4f91521306",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "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"
+ ],
+ "orders": [],
+ "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}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "kindergartenDiaryId",
+ "columnName": "kindergarten_diary_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "kindergarten_diary_id",
+ "semester_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "recipient",
+ "columnName": "recipient_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "removed",
+ "columnName": "removed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+ "fields": [
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "oneDriveId",
+ "columnName": "one_drive_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "real_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ReportingUnits",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shortName",
+ "columnName": "short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderName",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roles",
+ "columnName": "roles",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceId",
+ "columnName": "device_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDismissible",
+ "columnName": "is_dismissible",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "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, 'ac88c80d4bb923b22f22ce4f91521306')"
+ ]
+ }
+}
\ No newline at end of file
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 2f4c74fbf..24bb3917e 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
@@ -106,7 +106,8 @@ import javax.inject.Singleton
AdminMessage::class
],
autoMigrations = [
- AutoMigration(from = 44, to = 45)
+ AutoMigration(from = 44, to = 45),
+ AutoMigration(from = 46, to = 47),
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@@ -115,7 +116,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
- const val VERSION_SCHEMA = 46
+ const val VERSION_SCHEMA = 47
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt
index 3dd7ee0cb..187890c9b 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Semester.kt
@@ -7,7 +7,12 @@ import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.LocalDate
-@Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)])
+@Entity(
+ tableName = "Semesters", indices = [Index(
+ value = ["student_id", "diary_id", "kindergarten_diary_id", "semester_id"],
+ unique = true
+ )]
+)
data class Semester(
@ColumnInfo(name = "student_id")
@@ -16,6 +21,9 @@ data class Semester(
@ColumnInfo(name = "diary_id")
val diaryId: Int,
+ @ColumnInfo(name = "kindergarten_diary_id", defaultValue = "0")
+ val kindergartenDiaryId: Int,
+
@ColumnInfo(name = "diary_name")
val diaryName: String,
@@ -37,7 +45,7 @@ data class Semester(
@ColumnInfo(name = "unit_id")
val unitId: Int
-): Serializable {
+) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/SemesterMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/SemesterMapper.kt
index acd93a91a..67d68a1e3 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/SemesterMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/SemesterMapper.kt
@@ -7,6 +7,7 @@ fun List.mapToEntities(studentId: Int) = map {
Semester(
studentId = studentId,
diaryId = it.diaryId,
+ kindergartenDiaryId = it.kindergartenDiaryId,
diaryName = it.diaryName,
schoolYear = it.schoolYear,
semesterId = it.semesterId,
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
index ec9198175..7184f5576 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
@@ -7,13 +7,7 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Absent
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.monday
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.sunday
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
@@ -52,7 +46,8 @@ class AttendanceRepository @Inject constructor(
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
},
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getAttendance(start.monday, end.sunday, semester.semesterId)
.mapToEntities(semester)
},
@@ -90,7 +85,8 @@ class AttendanceRepository @Inject constructor(
timeId = attendance.timeId
)
}
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.excuseForAbsence(items, reason)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
index bc1fb2343..0857475f8 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
@@ -5,11 +5,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -38,7 +34,8 @@ class AttendanceSummaryRepository @Inject constructor(
},
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getAttendanceSummary(subjectId)
.mapToEntities(semester, subjectId)
},
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
index c2e5a7217..2055f3f47 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
@@ -5,13 +5,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.monday
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.sunday
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import javax.inject.Inject
@@ -51,7 +45,8 @@ class CompletedLessonsRepository @Inject constructor(
)
},
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getCompletedLessons(start.monday, end.sunday)
.mapToEntities(semester)
},
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
index a1667ccbc..6af24d73f 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
@@ -40,7 +40,8 @@ class ConferenceRepository @Inject constructor(
conferenceDb.loadAll(semester.diaryId, student.studentId, startDate)
},
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getConferences()
.mapToEntities(semester)
.filter { it.date >= startDate }
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
index 9bdac0658..c655c8001 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
@@ -6,13 +6,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.endExamsDay
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.startExamsDay
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
@@ -54,7 +48,8 @@ class ExamRepository @Inject constructor(
)
},
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getExams(start.startExamsDay, start.endExamsDay, semester.semesterId)
.mapToEntities(semester)
},
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
index d539c14b9..f4087a887 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
@@ -47,7 +47,7 @@ class GradeRepository @Inject constructor(
},
fetch = {
val (details, summary) = sdk.init(student)
- .switchDiary(semester.diaryId, semester.schoolYear)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getGrades(semester.semesterId)
details.mapToEntities(semester) to summary.mapToEntities(semester)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
index 6c36f163b..356c203d0 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
@@ -12,13 +12,9 @@ import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.sync.Mutex
-import java.util.Locale
+import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
@@ -54,7 +50,8 @@ class GradeStatisticsRepository @Inject constructor(
},
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getGradesPartialStatistics(semester.semesterId)
.mapToEntities(semester)
},
@@ -101,7 +98,8 @@ class GradeStatisticsRepository @Inject constructor(
},
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getGradesSemesterStatistics(semester.semesterId)
.mapToEntities(semester)
},
@@ -155,7 +153,8 @@ class GradeStatisticsRepository @Inject constructor(
},
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getGradesPointsStatistics(semester.semesterId)
.mapToEntities(semester)
},
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
index 95a375a45..900d9a68e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
@@ -6,13 +6,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.monday
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.sunday
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import javax.inject.Inject
@@ -53,7 +47,8 @@ class HomeworkRepository @Inject constructor(
)
},
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getHomework(start.monday, end.sunday)
.mapToEntities(semester)
},
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
index bf17cbbc5..f825c36df 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
@@ -8,11 +8,7 @@ import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -40,7 +36,8 @@ class MobileDeviceRepository @Inject constructor(
},
query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) },
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getRegisteredDevices()
.mapToEntities(semester)
},
@@ -53,14 +50,16 @@ class MobileDeviceRepository @Inject constructor(
)
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.unregisterDevice(device.deviceId)
mobileDb.deleteAll(listOf(device))
}
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {
- return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ return sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getToken()
.mapToMobileDeviceToken()
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
index 121324d8b..19ad8f037 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
@@ -38,7 +38,8 @@ class NoteRepository @Inject constructor(
},
query = { noteDb.loadAll(student.studentId) },
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getNotes(semester.semesterId)
.mapToEntities(semester)
},
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
index 288a1fb67..880a6a74c 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
@@ -38,7 +38,9 @@ class SchoolRepository @Inject constructor(
},
query = { schoolDb.load(semester.studentId, semester.classId) },
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool()
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
+ .getSchool()
.mapToEntity(semester)
},
saveFetchResult = { old, new ->
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt
index cc954558f..96f019223 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt
@@ -5,11 +5,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.DispatchersProvider
-import io.github.wulkanowy.utils.getCurrentOrLast
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.isCurrent
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
@@ -43,10 +39,14 @@ class SemesterRepository @Inject constructor(
): Boolean {
val isNoSemesters = semesters.isEmpty()
- val isRefreshOnModeChangeRequired =
- if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
- semesters.firstOrNull { it.isCurrent }?.diaryId == 0
- } else false
+ val isRefreshOnModeChangeRequired = when {
+ Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> {
+ semesters.firstOrNull { it.isCurrent }?.let {
+ 0 == it.diaryId && 0 == it.kindergartenDiaryId
+ } == true
+ }
+ else -> false
+ }
val isRefreshOnNoCurrentAppropriate =
refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent }
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
index e98daedf2..1fa91dd46 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
@@ -28,7 +28,8 @@ class StudentInfoRepository @Inject constructor(
shouldFetch = { it == null || forceRefresh },
query = { studentInfoDao.loadStudentInfo(student.studentId) },
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getStudentInfo().mapToEntity(semester)
},
saveFetchResult = { old, new ->
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
index d81cb7c92..b9bca028f 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
@@ -5,11 +5,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -37,7 +33,8 @@ class SubjectRepository @Inject constructor(
},
query = { subjectDao.loadAll(semester.diaryId, semester.studentId) },
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getSubjects().mapToEntities(semester)
},
saveFetchResult = { old, new ->
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
index 029b2707a..6b615c7a7 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
@@ -5,11 +5,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -37,7 +33,8 @@ class TeacherRepository @Inject constructor(
},
query = { teacherDb.loadAll(semester.studentId, semester.classId) },
fetch = {
- sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+ sdk.init(student)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getTeachers(semester.semesterId)
.mapToEntities(semester)
},
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
index 1f7bb1cff..7534640c3 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
@@ -3,22 +3,12 @@ package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
-import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.db.entities.Timetable
-import io.github.wulkanowy.data.db.entities.TimetableAdditional
-import io.github.wulkanowy.data.db.entities.TimetableHeader
+import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.pojos.TimetableFull
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.monday
-import io.github.wulkanowy.utils.networkBoundResource
-import io.github.wulkanowy.utils.sunday
-import io.github.wulkanowy.utils.uniqueSubtract
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.sync.Mutex
@@ -62,7 +52,7 @@ class TimetableRepository @Inject constructor(
query = { getFullTimetableFromDatabase(student, semester, start, end) },
fetch = {
val timetableFull = sdk.init(student)
- .switchDiary(semester.diaryId, semester.schoolYear)
+ .switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getTimetableFull(start.monday, end.sunday)
timetableFull.mapToEntities(semester)
diff --git a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
index 89ccade1b..225399306 100644
--- a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
+++ b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
@@ -10,6 +10,7 @@ import io.github.wulkanowy.sdk.pojo.Semester as SdkSemester
fun getSemesterEntity(diaryId: Int = 1, semesterId: Int = 1, start: LocalDate = LocalDate.now(), end: LocalDate = LocalDate.now(), semesterName: Int = 1) = Semester(
studentId = 1,
diaryId = diaryId,
+ kindergartenDiaryId = 0,
semesterId = semesterId,
diaryName = "$semesterId",
schoolYear = 1970,
@@ -22,6 +23,7 @@ fun getSemesterEntity(diaryId: Int = 1, semesterId: Int = 1, start: LocalDate =
fun getSemesterPojo(diaryId: Int, semesterId: Int, start: LocalDate, end: LocalDate, semesterName: Int = 1) = SdkSemester(
diaryId = diaryId,
+ kindergartenDiaryId = 0,
semesterId = semesterId,
diaryName = "$semesterId",
schoolYear = 1970,
diff --git a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
index 261a6c1cf..bdfb4137d 100644
--- a/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/db/migrations/Migration13Test.kt
@@ -159,6 +159,7 @@ class Migration13Test : AbstractMigrationTest() {
semesters.add(Semester(
studentId = it.getInt(1),
diaryId = it.getInt(2),
+ kindergartenDiaryId = 0,
diaryName = it.getString(3),
semesterId = it.getInt(4),
semesterName = it.getInt(5),
From a03bcf8e62a06295d22216e473a8247269b36fca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 31 Dec 2021 12:36:14 +0100
Subject: [PATCH 025/669] Display comment after entry in grade notifications
(#1741)
---
.../services/sync/notifications/NewGradeNotification.kt | 5 ++++-
.../ui/modules/debug/notification/mock/gradeDetails.kt | 6 +++---
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt
index 39ecbe33d..32d2ba6a0 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt
@@ -22,7 +22,10 @@ class NewGradeNotification @Inject constructor(
val notificationDataList = items.map {
NotificationData(
title = context.getPlural(R.plurals.grade_new_items, 1),
- content = "${it.subject}: ${it.entry}",
+ content = buildString {
+ append("${it.subject}: ${it.entry}")
+ if (it.comment.isNotBlank()) append(" (${it.comment})")
+ },
intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDetails.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDetails.kt
index f9c481e39..77b60188b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDetails.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/gradeDetails.kt
@@ -5,7 +5,7 @@ import java.time.LocalDate
val debugGradeDetailsItems = listOf(
generateGrade("Matematyka", "+"),
- generateGrade("Matematyka", "2="),
+ generateGrade("Matematyka", "120", comment = "%"),
generateGrade("Fizyka", "-"),
generateGrade("Geografia", "4+"),
generateGrade("Sieci komputerowe", "1"),
@@ -17,14 +17,14 @@ val debugGradeDetailsItems = listOf(
generateGrade("Wychowanie fizyczne", "5"),
)
-private fun generateGrade(subject: String, entry: String) = Grade(
+private fun generateGrade(subject: String, entry: String, comment: String = "") = Grade(
subject = subject,
entry = entry,
semesterId = 0,
studentId = 0,
value = 0.0,
modifier = 0.0,
- comment = "",
+ comment = comment,
color = "",
gradeSymbol = "",
description = "",
From bc672e94f848597823e2d1b4dbdd2a002868d08e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 1 Jan 2022 13:48:58 +0100
Subject: [PATCH 026/669] Strip html from school announcements notifications
(#1743)
---
app/build.gradle | 2 +-
.../sync/notifications/NewSchoolAnnouncementNotification.kt | 3 ++-
.../ui/modules/debug/notification/mock/schoolAnnouncement.kt | 4 ++--
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index b7a075b98..840229297 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -174,7 +174,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:f6f32b755a"
+ implementation "io.github.wulkanowy:sdk:6e1b8eb26a"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt
index 6b839d298..695438a70 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt
@@ -1,6 +1,7 @@
package io.github.wulkanowy.services.sync.notifications
import android.content.Context
+import androidx.core.text.parseAsHtml
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
@@ -28,7 +29,7 @@ class NewSchoolAnnouncementNotification @Inject constructor(
R.plurals.school_announcement_notify_new_item_title,
1
),
- content = "${it.subject}: ${it.content}"
+ content = "${it.subject}: ${it.content.parseAsHtml()}"
)
}
val groupNotificationData = GroupNotificationData(
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt
index 42524e6e0..9b21f08e6 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt
@@ -4,13 +4,13 @@ import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
import java.time.LocalDate
val debugSchoolAnnouncementItems = listOf(
- generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych\n03.05.2021 - poniedziałek"),
+ generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych
03.05.2021 – poniedziałek"),
generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do noszenia maseczek"),
generateAnnouncement("Święto szkoły", "W najbliższych dniach obchodzimy święto szkoły, podczas którego..."),
generateAnnouncement("Rocznica odzyskania przez szkołę sztandaru", "Juz niedługo, bo za tydzień, a dokładnie za 8 dni..."),
generateAnnouncement("Ogłoszenie w sprawie otwarcia stołówki", "Wszyscy uczniowie zainteresowani obiadami w szkole..."),
generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie pilnym"),
- generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych\n21.06.2021 - poniedziałek"),
+ generateAnnouncement("Dzień wolny od zajęć dydaktycznych", "Dzień wolny od zajęć dydaktycznych
21.06.2021 – poniedziałek"),
generateAnnouncement("Zasady bezpieczeństwa", "Wszyscy uczniowie są zobowiązani do zdjęcia maseczek"),
generateAnnouncement("Święto państwowe", "W najbliższych dniach obchodzimy święto państwowe, podczas którego..."),
generateAnnouncement("Uczniowie proszeni do sekretariatu", "Kuba i Jacek z klasy czwartej proszeni do dyrektora w trybie wolnym"),
From 88773223575820556238078c76a1129263550433 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 1 Jan 2022 13:52:51 +0100
Subject: [PATCH 027/669] Differentiate school announcements by userLoginId
(#1744)
---
.../github/wulkanowy/data/mappers/DirectorInformationMapper.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt
index e6bf000b1..d059db816 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt
@@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformatio
fun List.mapToEntities(student: Student) = map {
SchoolAnnouncement(
- studentId = student.studentId,
+ studentId = student.userLoginId,
date = it.date,
subject = it.subject,
content = it.content,
From aff1a7030d2d6203a3ae86b14e832660dec14fe4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 1 Jan 2022 15:46:08 +0100
Subject: [PATCH 028/669] Add a custom error message for ssl errors due to
invalid clock setting (#1742)
---
.../github/wulkanowy/ui/base/ErrorDialog.kt | 24 +-----
.../github/wulkanowy/ui/base/ErrorHandler.kt | 4 +-
.../ui/modules/dashboard/DashboardFragment.kt | 9 +--
.../modules/dashboard/DashboardPresenter.kt | 1 +
.../ui/modules/dashboard/DashboardView.kt | 2 +-
.../wulkanowy/utils/ExceptionExtension.kt | 74 +++++++++++++++++++
.../wulkanowy/utils/ResourcesExtension.kt | 34 ---------
app/src/main/res/values/strings.xml | 1 +
8 files changed, 84 insertions(+), 65 deletions(-)
create mode 100644 app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
delete mode 100644 app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
index c2ffff1f5..48c003b7e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
@@ -16,15 +16,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.DialogErrorBinding
-import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
-import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
-import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
import io.github.wulkanowy.utils.*
-import okhttp3.internal.http2.StreamResetException
-import java.io.InterruptedIOException
-import java.net.ConnectException
-import java.net.SocketTimeoutException
-import java.net.UnknownHostException
import javax.inject.Inject
@AndroidEntryPoint
@@ -67,7 +59,7 @@ class ErrorDialog : DialogFragment() {
private fun DialogErrorBinding.bindErrorDetails(error: Throwable) {
return with(this) {
- errorDialogHumanizedMessage.text = resources.getString(error)
+ errorDialogHumanizedMessage.text = resources.getErrorString(error)
errorDialogErrorMessage.text = error.localizedMessage
errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank()
errorDialogContent.text = error.stackTraceToString()
@@ -77,22 +69,10 @@ class ErrorDialog : DialogFragment() {
private fun AlertDialog.enableReportButtonIfErrorIsReportable(error: Throwable) {
setOnShowListener {
- getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = isErrorShouldBeReported(error)
+ getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported()
}
}
- private fun isErrorShouldBeReported(error: Throwable): Boolean = when (error) {
- is UnknownHostException,
- is InterruptedIOException,
- is ConnectException,
- is StreamResetException,
- is SocketTimeoutException,
- is ServiceUnavailableException,
- is FeatureDisabledException,
- is FeatureNotAvailableException -> false
- else -> true
- }
-
private fun copyErrorToClipboard(errorStacktrace: String) {
val clip = ClipData.newPlainText("Error details", errorStacktrace)
requireActivity().getSystemService()?.setPrimaryClip(clip)
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 fbc994e2c..afe200e9a 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
@@ -5,7 +5,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
-import io.github.wulkanowy.utils.getString
+import io.github.wulkanowy.utils.getErrorString
import io.github.wulkanowy.utils.security.ScramblerException
import timber.log.Timber
import javax.inject.Inject
@@ -26,7 +26,7 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
}
protected open fun proceed(error: Throwable) {
- showErrorMessage(context.resources.getString(error), error)
+ showErrorMessage(context.resources.getErrorString(error), error)
when (error) {
is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl)
is ScramblerException, is BadCredentialsException -> onSessionExpired()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
index 12a28ea73..88c281ecd 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
@@ -28,10 +28,7 @@ import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.notificationscenter.NotificationsCenterFragment
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
-import io.github.wulkanowy.utils.capitalise
-import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.openInternetBrowser
-import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.utils.*
import java.time.LocalDate
import javax.inject.Inject
@@ -178,8 +175,8 @@ class DashboardFragment : BaseFragment(R.layout.fragme
binding.dashboardErrorContainer.isVisible = show
}
- override fun setErrorDetails(message: String) {
- binding.dashboardErrorMessage.text = message
+ override fun setErrorDetails(error: Throwable) {
+ binding.dashboardErrorMessage.text = requireContext().resources.getErrorString(error)
}
override fun resetView() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index 360d6be84..a1845ab59 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -714,6 +714,7 @@ class DashboardPresenter @Inject constructor(
if ((forceRefresh && wasGeneralError) || !forceRefresh) {
showContent(false)
showErrorView(true)
+ setErrorDetails(lastError)
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt
index 730e19a35..2cc2f1d2d 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt
@@ -18,7 +18,7 @@ interface DashboardView : BaseView {
fun showErrorView(show: Boolean)
- fun setErrorDetails(message: String)
+ fun setErrorDetails(error: Throwable)
fun resetView()
diff --git a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
new file mode 100644
index 000000000..43cecd400
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
@@ -0,0 +1,74 @@
+package io.github.wulkanowy.utils
+
+import android.content.res.Resources
+import io.github.wulkanowy.R
+import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
+import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
+import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException
+import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
+import io.github.wulkanowy.sdk.scrapper.exception.VulcanException
+import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
+import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
+import okhttp3.internal.http2.StreamResetException
+import java.io.InterruptedIOException
+import java.net.ConnectException
+import java.net.SocketException
+import java.net.SocketTimeoutException
+import java.net.UnknownHostException
+import java.security.cert.CertificateExpiredException
+import java.security.cert.CertificateNotYetValidException
+import javax.net.ssl.SSLHandshakeException
+
+fun Resources.getErrorString(error: Throwable): String = when (error) {
+ is UnknownHostException -> R.string.error_no_internet
+ is SocketException,
+ is SocketTimeoutException,
+ is InterruptedIOException,
+ is ConnectException,
+ is StreamResetException -> R.string.error_timeout
+ is NotLoggedInException -> R.string.error_login_failed
+ is PasswordChangeRequiredException -> R.string.error_password_change_required
+ is ServiceUnavailableException -> R.string.error_service_unavailable
+ is FeatureDisabledException -> R.string.error_feature_disabled
+ is FeatureNotAvailableException -> R.string.error_feature_not_available
+ is VulcanException -> R.string.error_unknown_uonet
+ is ScrapperException -> R.string.error_unknown_app
+ is SSLHandshakeException -> when {
+ error.isCausedByCertificateNotValidNow() -> R.string.error_invalid_device_datetime
+ else -> R.string.error_timeout
+ }
+ else -> R.string.error_unknown
+}.let { getString(it) }
+
+fun Throwable.isShouldBeReported(): Boolean = when (this) {
+ is UnknownHostException,
+ is SocketException,
+ is SocketTimeoutException,
+ is InterruptedIOException,
+ is ConnectException,
+ is StreamResetException,
+ is ServiceUnavailableException,
+ is FeatureDisabledException,
+ is FeatureNotAvailableException -> false
+ is SSLHandshakeException -> when {
+ isCausedByCertificateNotValidNow() -> false
+ else -> true
+ }
+ else -> true
+}
+
+private fun Throwable?.isCausedByCertificateNotValidNow(): Boolean {
+ var exception = this
+ do {
+ if (exception.isCertificateNotValidNow()) return true
+
+ exception = exception?.cause
+ } while (exception != null)
+ return false
+}
+
+private fun Throwable?.isCertificateNotValidNow(): Boolean {
+ val isNotYetValid = this is CertificateNotYetValidException
+ val isExpired = this is CertificateExpiredException
+ return isNotYetValid || isExpired
+}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt
deleted file mode 100644
index 71d3fd173..000000000
--- a/app/src/main/java/io/github/wulkanowy/utils/ResourcesExtension.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package io.github.wulkanowy.utils
-
-import android.content.res.Resources
-import io.github.wulkanowy.R
-import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
-import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
-import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException
-import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
-import io.github.wulkanowy.sdk.scrapper.exception.VulcanException
-import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
-import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
-import okhttp3.internal.http2.StreamResetException
-import java.io.InterruptedIOException
-import java.net.ConnectException
-import java.net.SocketException
-import java.net.SocketTimeoutException
-import java.net.UnknownHostException
-
-fun Resources.getString(error: Throwable) = when (error) {
- is UnknownHostException -> getString(R.string.error_no_internet)
- is SocketException,
- is SocketTimeoutException,
- is InterruptedIOException,
- is ConnectException,
- is StreamResetException -> getString(R.string.error_timeout)
- is NotLoggedInException -> getString(R.string.error_login_failed)
- is PasswordChangeRequiredException -> getString(R.string.error_password_change_required)
- is ServiceUnavailableException -> getString(R.string.error_service_unavailable)
- is FeatureDisabledException -> getString(R.string.error_feature_disabled)
- is FeatureNotAvailableException -> getString(R.string.error_feature_not_available)
- is VulcanException -> getString(R.string.error_unknown_uonet)
- is ScrapperException -> getString(R.string.error_unknown_app)
- else -> getString(R.string.error_unknown)
-}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bdf2935bd..9c63073e2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -767,6 +767,7 @@
No internet connection
+ An error occurred. Check your device clock
Connection to register failed. Servers can be overloaded. Please try again later
Loading data failed. Please try again later
Register password change required
From 2bb2190410671dc5edaba4e541930ba1a41f72c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sat, 1 Jan 2022 17:48:58 +0100
Subject: [PATCH 029/669] New Crowdin updates (#1745)
---
app/src/main/res/values-cs/strings.xml | 1 +
.../main/res/values-de/preferences_values.xml | 6 +-
app/src/main/res/values-de/strings.xml | 125 +++++++++---------
app/src/main/res/values-pl/strings.xml | 1 +
app/src/main/res/values-ru/strings.xml | 1 +
app/src/main/res/values-sk/strings.xml | 1 +
app/src/main/res/values-uk/strings.xml | 1 +
7 files changed, 71 insertions(+), 65 deletions(-)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 3d9cdd9a7..b56617ed7 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -760,6 +760,7 @@
Aktualizace selhala! Wulkanowy nemusí fungovat správně. Zvažte aktualizaci
Žádné internetové připojení
+ Vyskytla se chyba. Zkontrolujte hodiny svého zařízení
Nelze se připojit ke deníku. Servery mohou být přetíženy. Prosím zkuste to znovu později
Načítání dat se nezdařilo. Prosím zkuste to znovu později
Je vyžadována změna hesla pro deník
diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml
index 23e54cb78..08b9d240b 100644
--- a/app/src/main/res/values-de/preferences_values.xml
+++ b/app/src/main/res/values-de/preferences_values.xml
@@ -41,9 +41,9 @@
- Farben der Bewertungen im Logbuch
- - Up to 1 at once
- - Always expanded
- - Unlimited expansions
+ - Bis zu 1 auf einmal
+ - Immer erweitert
+ - Unbegrenzte Erweiterungen
- Durchschnitt der Noten aus beiden Semestern
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 9a6e3e662..931f643a6 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -6,7 +6,7 @@
Noten
Schulbesuch
Prüfungen
- Stundenplan
+ Zeitplan
Einstellungen
Mehr
Über die Applikation
@@ -17,7 +17,7 @@
Lizenzen
Nachrichten
neue Nachricht
- New homework
+ Neue Hausaufgaben
Eintragen und Erfolgen
Hausaufgaben
Konten-Manager
@@ -56,7 +56,7 @@
Schüler nicht gefunden. Überprüfen Sie das Symbol und die gewählte Variation des UONET+ Registers
Ausgewählter Student ist bereits angemeldet.
Das Symbol kann auf der Registerseite in Uczeń→ Dostęp Mobilny → Zarejestruj urządzenie mobilnegefunden werden.\n\nStellen Sie sicher, dass Sie die entsprechende Registervariante im Feld UONET+ Registervariante auf dem vorherigen Bildschirm festgelegt haben. Wulkanowy erkennt zur Zeit keine Vorschulstudenten
- Wählen Sie die Studenten aus, die sich bei der Anwendung anmelden sollen.
+ Wählen Sie die Studenten aus, die sich bei der Anwendung anmelden sollen
Andere Optionen
In diesem Modus funktioniert eine Glücknummer, eine Klassenstatistik, eine Zusammenfassung der Anwesenheit, eine Entschuldigung für die Abwesenheit, abgeschlossene Lektionen, Schulinformationen und eine Vorschau der Liste der registrierten Geräte nicht
In diesem Modus werden dieselben Daten angezeigt, die auf der Klassenbuch-Website angezeigt werden
@@ -95,7 +95,7 @@
Wie funktioniert der berechnete Durchschnitt?
The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n3. Adding calculated averages\n4. Calculating the arithmetic average of summed averages
Wie funktioniert der endgültige Durchschnitt?
- The Final Average is the arithmetic average calculated from all currently available final grades in the given semester.\n\nThe calculation scheme consists of the following steps:\n1. Summing up the final grades given by teachers\n2. Divide by the number of subjects that have already been graded
+ Der Final Average ist das arithmetische Mittel, das aus allen derzeit verfügbaren Abschlussnoten des jeweiligen Semesters berechnet wird. \n\nDas Berechnungsschema besteht aus folgenden Schritten:\n1. Zusammenfassung der von den Lehrern gegebenen Abschlussnoten\n2. Division durch die Anzahl der Fächer, die bereits bewertet wurden
Finaler Durchschnitt
aus %1$d von %2$d Schulfächern
Zusammenfassung
@@ -151,25 +151,25 @@
Jetzt: %s
In einem Moment: %s
Später: %s
- %1$s lesson %2$d - %3$s
- Change of room from %1$s to %2$s
- Change of teacher from %1$s to %2$s
- Change of subject from %1$s to %2$s
+ %1$s Lektion %2$d - %3$s
+ Änderung des Raumes von %1$s zu %2$s
+ Wechsel des Lehrers von %1$s zu %2$s
+ Thema von %1$s zu %2$s wechseln
- - Timetable change
- - Timetable changes
+ - Änderung des Zeitplans
+ - Änderungen des Zeitplans
- - %1$s - %2$d change in timetable
- - %1$s - %2$d changes in timetable
+ - %1$s – %2$d Änderung im Zeitplan
+ - %1$s - %2$d Änderungen im Zeitplan
- - %1$d change in timetable
- - %1$d changes in timetable
+ - %1$d Änderung im Zeitplan
+ - %1$d Änderungen im Zeitplan
- - %d change
- - %d changes
+ - %d Änderung
+ - %d Änderungen
Beendete Lektionen
@@ -182,17 +182,17 @@
Zusätzliche Lektionen
Zusätzliche Lektionen anzeigen
Keine Informationen über zusätzlichen Lektionen
- New lesson
- New additional lesson
- Additional lesson added successfully
- Additional lesson deleted successfully
- Repeat weekly
- Delete additional lesson
- Just this lesson
- All in the series
- Start time
- End time
- End time must be greater than start time
+ Neue Lektion
+ Neue zusätzliche Lektion
+ Zusätzliche Lektion erfolgreich hinzugefügt
+ Zusätzliche Lektion erfolgreich gelöscht
+ Wöchentlich wiederholen
+ Zusätzliche Lektion löschen
+ Nur diese Lektion
+ Alle in der Reihe
+ Startzeit
+ Endzeit
+ Endzeit muss grösser sein als Startzeit
Übersicht über die Schulbesuch
Aus schulischen Gründen abwesend
@@ -212,16 +212,16 @@
Sie müssen mindestens eine Abwesenheit auswählen!
Verzeihung
- - New attendance
- - New attendance
+ - Neue Teilnehmerzahl
+ - Neue Teilnehmerzahl
- - %1$d new attendance
- - %1$d attendance
+ - %1$d neue Teilnahme
+ - %1$d Teilnahme
- - %d attendance
- - %d attendance
+ - %d Teilnahme
+ - %d Teilnahme
Gesamt
@@ -234,8 +234,8 @@
- Neue prüfungen
- - %d new exam
- - %d new exams
+ - %d neue Prüfung
+ - %d neue Prüfungen
- %d prüfung
@@ -327,9 +327,9 @@
Keine Informationen über Hausaufgaben
Gemacht
Unvollständig
- Add homework
- Homework added successfully
- Homework deleted successfully
+ Hausaufgaben hinzufügen
+ Hausaufgaben erfolgreich hinzugefügt
+ Heimarbeit erfolgreich gelöscht
Anhänge
- Neue hausaufgaben
@@ -483,7 +483,7 @@
Lektionen
(Morgen)
- (Today and tomorrow)
+ (Heute und morgen)
Gleich:
Bald:
Erstens:
@@ -557,7 +557,7 @@
Nein
Speichern
Titel
- Add
+ Hinzufügen
Kopiert
lösen
Ändern
@@ -574,7 +574,7 @@
Mittelwertberechnung durch App erzwingen
Anwesendheit zeigen
Thema
- Grades expanding
+ Steigende Sorten
Aktuelle Lektion markieren
Gruppen neben Schulfächen anzeigen
Liste der Diagramme in Klassenbewertungen anzeigen
@@ -583,7 +583,7 @@
Schulfachen sortieren
Sprache
Benachrichtigungen
- Other
+ Sonstiges
Benachrichtigungen anzeigen
Benachrichtigungen über bevorstehende Lektionen anzeigen
Festlegen einer Benachrichtigung über die bevorstehende Lektion dauerhaft
@@ -593,14 +593,14 @@
Ihr Gerät hat möglicherweise Probleme mit der Datensynchronisierung und Benachrichtigungen.\n\nUm diese zu reparieren, fügen Sie Wulkanowy zum Autostart hinzu und deaktivieren Sie die Batterieoptimierung in den Systemeinstellungen des Geräts.
Debug-Benachrichtigungen anzeigen
Synchronisierung ist deaktiviert
- Official app notifications
+ Offizielle Benachrichtigungen
Offizielle App-Benachrichtigungen erfassen
- Remove official app notifications after capture
+ Entfernen Sie offizielle App-Benachrichtigungen nach der Erfassung
Benachrichtigungen erfassen
With this feature you can gain a substitute of push notifications like in the official app. All you need to do is allow Wulkanowy to receive all notifications in your system settings.\n\nHow it works?\nWhen you get a notification in Dziennik VULCAN, Wulkanowy will be notified (that\'s what these extra permissions are for) and will trigger a sync so that can send its own notification.\n\nFOR ADVANCED USERS ONLY
- Upcoming lesson notifications
- You must allow the Wulkanowy app to set alarms and reminders in your system settings to use this feature.
- Go to settings
+ Bevorstehende Unterrichtsbenachrichtigungen
+ Sie müssen der Wulkanowy-App erlauben, in Ihren Systemeinstellungen Alarme und Erinnerungen einzustellen, damit diese Funktion verwendet werden kann.
+ Gehe zu den Einstellungen
Synchronisierung
Automatische Aktualisierung
An Feiertagen suspendiert
@@ -615,26 +615,26 @@
Wert des Minus
Antwort mit Nachrichtenhistorie
Arithmetisches Mittel anzeigen, wenn keine Gewichte angegeben sind
- Support
- Watch single ad to support project
- Consent to data processing
- To view an advertisement you must agree to the data processing terms of our Privacy Policy
- Agree
- Privacy policy
- Ad is loading
- Thank you for your support, come back later for more ads
+ Unterstützung
+ Einzelanzeige ansehen, um Projekt zu unterstützen
+ Einwilligung in die Datenverarbeitung
+ Um eine Anzeige zu sehen, müssen Sie mit den Datenverarbeitungsbedingungen unserer Datenschutzerklärung einverstanden sein
+ Einverstanden
+ Datenschutzerklärung
+ Anzeige wird geladen
+ Vielen Dank für Ihre Unterstützung, kommen Sie später wieder für weitere Anzeigen
Erweitert
Aussehen & Verhalten
Benachrichtigungen
Synchronisierung
- Advertisements
+ Werbung
Noten
Dashboard
Sichtbarkeit der Kacheln
Schulbesuch
Stundenplan
Noten
- Calculated average
+ Berechneter Durchschnitt
Nachrichten
Aussehen & Verhalten
Sprachen, Themen, Schulfachen sortieren
@@ -644,8 +644,8 @@
Automatisches Update, Synchronisierungsintervall
Plus und Minus Werte, Durchschnittsberechnung
Erweitert
- App version, contributors, social portals
- Displaying advertisements, project support
+ App-Version, Mitwirkende, soziale Portale
+ Anzeigen, Projektunterstützung
Neue Noten
Neue Hausaufgaben
@@ -658,8 +658,8 @@
Push-Benachrichtigungen
Bevorstehende Lektionen
Debuggen
- Timetable change
- New attendance
+ Änderung des Zeitplans
+ Neue Teilnehmerzahl
Schwarz
Rot
@@ -674,6 +674,7 @@
Update fehlgeschlagen! Wulkanowy funktioniert möglicherweise nicht richtig. Überlegen Sie die Aktualisierung
Keine Internetverbindung
+ Es ist ein Fehler aufgetreten. Überprüfen Sie Ihre Geräteuhr
Registrierungsverbindung fehlgeschlagen. Server können überlastet sein. Bitte versuchen Sie es später noch einmal
Das Laden der Daten ist fehlgeschlagen. Bitte versuchen Sie es später noch einmal
Passwortänderung für Registrierung erforderlich
@@ -683,5 +684,5 @@
Ein unerwarteter Fehler ist aufgetreten
Funktion, die von Ihrer Schule deaktiviert wurde
Feature in diesem Modus nicht verfügbar
- This field is required
+ Dieses Feld ist erforderlich
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index ff21b6446..208e6f3ea 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -760,6 +760,7 @@
Aktualizacja nie powiodła się! Wulkanowy może nie działać prawidłowo. Rozważ aktualizację
Brak połączenia z internetem
+ Wystąpił błąd. Sprawdź poprawność daty w urządzeniu
Nie udało się połączyć z dziennikiem. Serwery mogą być przeciążone. Spróbuj ponownie później
Ładowanie danych nie powiodło się. Spróbuj ponownie później
Wymagana zmiana hasła do dziennika
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 87cae3ea7..b5ad3683e 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -760,6 +760,7 @@
Не удалось обновить! Wulkanowy может работать некорректно. Рассмотрите возможность обновления
Нет интернет-подключения
+ An error occurred. Check your device clock
Не удалось подключиться к регистрации. Серверы могут быть перегружены. Пожалуйста, повторите попытку позже
Не удалось загрузить данные. Пожалуйста, повторите попытку позже
Необходимо изменить пароль реестра
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 3698fce92..30365fe56 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -760,6 +760,7 @@
Aktualizácia zlyhala! Wulkanowy nemusí fungovať správne. Zvážte aktualizáciu
Žiadne internetové pripojenie
+ Vyskytla sa chyba. Skontrolujte hodiny svojho zariadenia
Nedá sa pripojiť ku denníku. Servery môžu byť preťažené. Prosím skúste to znova neskôr
Načítanie údajov zlyhalo. Skúste neskôr prosím
Je vyžadovaná zmena hesla pre denník
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index f3f749cf9..0c88df317 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -760,6 +760,7 @@
Помилка оновлення! Wulkanowy може не працювати належним чином. Подумайте про оновлення
Брак з\'єднання з інтернетом
+ An error occurred. Check your device clock
Помилка підключення до реєстрації. Сервери можуть бути перевантажені. Будь-ласка спробуйте пізніше
Помилка завантаження даних. Будь-ласка спробуйте пізніше
Потрібна реєстрація зміни пароля
From daf97be9ad7e907cda532069c60162039a02615f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 1 Jan 2022 17:50:02 +0100
Subject: [PATCH 030/669] Version 1.5.0
---
app/build.gradle | 12 +++++++-----
app/src/main/play/release-notes/pl-PL/default.txt | 8 +++++---
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 840229297..41be0d50e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -22,8 +22,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 31
- versionCode 102
- versionName "1.4.4"
+ versionCode 103
+ versionName "1.5.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -149,8 +149,10 @@ kapt {
play {
defaultToAppBundles = false
- track = 'beta'
- updatePriority = 4
+ track = 'production'
+ releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
+ userFraction = 0.25d
+ updatePriority = 1
enabled.set(false)
}
@@ -174,7 +176,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:6e1b8eb26a"
+ implementation "io.github.wulkanowy:sdk:1.5.0"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
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 f40e3fc33..6e768ac3a 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 1.4.4
+Wersja 1.5.0
-- naprawiliśmy logowanie do Gdańskiej Platformy Edukacyjnej
-- naprawiliśmy sortowanie ocen oraz ogłoszeń
+- dodaliśmy możliwość dodawania własnych lekcji dodatkowych
+- dodaliśmy wsparcie dla różnych stref czasowych
+- dodaliśmy eksperymentalne wsparcie dla przedszkola
+- wprowadziliśmy też wiele innych mniejszych poprawek, poprawiających komfort używania aplikacji
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
From d5cc2263f52378c9d285fabe1e734cd30916852c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 4 Jan 2022 10:13:31 +0000
Subject: [PATCH 031/669] Bump mockk from 1.12.1 to 1.12.2 (#1747)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 41be0d50e..edf96b7ff 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -171,7 +171,7 @@ ext {
android_hilt = "1.0.0"
room = "2.4.0"
chucker = "3.5.2"
- mockk = "1.12.1"
+ mockk = "1.12.2"
coroutines = "1.6.0"
}
From 210308695b1fd2ca12839edeff9ab6297b5cb020 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 11 Jan 2022 11:12:48 +0000
Subject: [PATCH 032/669] Bump huawei-publish-gradle-plugin from 1.3.0 to 1.3.1
(#1750)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index b06af2e16..b91320f7d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,7 +19,7 @@ buildscript {
classpath 'com.huawei.agconnect:agcp:1.6.3.200'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
- classpath "ru.cian:huawei-publish-gradle-plugin:1.3.0"
+ classpath "ru.cian:huawei-publish-gradle-plugin:1.3.1"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3"
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries"
From 14ebdad7b24dea72b41a35b965a0e20288eed5ac Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 23 Jan 2022 00:55:58 +0000
Subject: [PATCH 033/669] Bump constraintlayout from 2.1.2 to 2.1.3 (#1761)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index edf96b7ff..39a63eab3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -194,7 +194,7 @@ dependencies {
implementation "androidx.recyclerview:recyclerview:1.2.1"
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
- implementation "androidx.constraintlayout:constraintlayout:2.1.2"
+ implementation "androidx.constraintlayout:constraintlayout:2.1.3"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
implementation "com.google.android.material:material:1.4.0"
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
From c9b506ae10d4fb14bb8d77ce7acf75f8b46e8cfa Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 23 Jan 2022 00:56:16 +0000
Subject: [PATCH 034/669] Bump agcp from 1.6.3.200 to 1.6.3.300 (#1760)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index b91320f7d..6c0e709d2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.0.4'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
- classpath 'com.huawei.agconnect:agcp:1.6.3.200'
+ classpath 'com.huawei.agconnect:agcp:1.6.3.300'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.1"
From 90e1cea679dcbc1b7b3f496425ec7cc18804c534 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 23 Jan 2022 00:56:37 +0000
Subject: [PATCH 035/669] Bump appcompat from 1.4.0 to 1.4.1 (#1759)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 39a63eab3..ec2447352 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -186,7 +186,7 @@ dependencies {
implementation "androidx.core:core-ktx:1.7.0"
implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'
implementation "androidx.activity:activity-ktx:1.4.0"
- implementation "androidx.appcompat:appcompat:1.4.0"
+ implementation "androidx.appcompat:appcompat:1.4.1"
implementation "androidx.fragment:fragment-ktx:1.4.0"
implementation "androidx.annotation:annotation:1.3.0"
From 5146e44574888aadb012e18ec8c81a33c362d3b8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 23 Jan 2022 00:56:57 +0000
Subject: [PATCH 036/669] Bump agconnect-crash from 1.6.3.200 to 1.6.3.300
(#1758)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index ec2447352..5a66b4c4c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -240,7 +240,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:20.5.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.3.2.300'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.3.200'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.3.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From b52a6f7f61e81dc93432732dde89ea519434c10a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 23 Jan 2022 00:57:17 +0000
Subject: [PATCH 037/669] Bump room from 2.4.0 to 2.4.1 (#1755)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 5a66b4c4c..2c0e33fd8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -169,7 +169,7 @@ huaweiPublish {
ext {
work_manager = "2.7.1"
android_hilt = "1.0.0"
- room = "2.4.0"
+ room = "2.4.1"
chucker = "3.5.2"
mockk = "1.12.2"
coroutines = "1.6.0"
From a00f2dcbda5194bf96e9cd43e349ef44d37de818 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 23 Jan 2022 00:57:40 +0000
Subject: [PATCH 038/669] Bump core from 1.10.2 to 1.10.3 (#1753)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 2c0e33fd8..4751e9ce2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -235,7 +235,7 @@ dependencies {
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
- playImplementation 'com.google.android.play:core:1.10.2'
+ playImplementation 'com.google.android.play:core:1.10.3'
playImplementation 'com.google.android.play:core-ktx:1.8.1'
playImplementation 'com.google.android.gms:play-services-ads:20.5.0'
From 5d1085a64abad49066606b2ba1c71b07cfc861dd Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 23 Jan 2022 00:58:12 +0000
Subject: [PATCH 039/669] Bump fuzzywuzzy from 1.3.1 to 1.3.3 (#1754)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 4751e9ce2..d0866b74b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -228,7 +228,7 @@ dependencies {
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
implementation "io.coil-kt:coil:1.4.0"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
- implementation 'me.xdrop:fuzzywuzzy:1.3.1'
+ implementation 'me.xdrop:fuzzywuzzy:1.3.3'
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
playImplementation platform('com.google.firebase:firebase-bom:29.0.3')
From ce4157933fa5ccf30bcf5d2602625a5397223078 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 23 Jan 2022 01:39:16 +0000
Subject: [PATCH 040/669] Bump core-splashscreen from 1.0.0-alpha02 to
1.0.0-beta01 (#1752)
---
app/build.gradle | 2 +-
.../io/github/wulkanowy/ui/modules/splash/SplashActivity.kt | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index d0866b74b..96138883f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -184,7 +184,7 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
implementation "androidx.core:core-ktx:1.7.0"
- implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'
+ implementation 'androidx.core:core-splashscreen:1.0.0-beta01'
implementation "androidx.activity:activity-ktx:1.4.0"
implementation "androidx.appcompat:appcompat:1.4.1"
implementation "androidx.fragment:fragment-ktx:1.4.0"
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt
index 5c1524551..a86024e49 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt
@@ -42,7 +42,7 @@ class SplashActivity : BaseActivity(), SplashView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- installSplashScreen().setKeepVisibleCondition { true }
+ installSplashScreen().setKeepOnScreenCondition { true }
val externalLink = intent?.getStringExtra(EXTRA_EXTERNAL_URL)
val startDestination = intent?.getSerializableExtra(EXTRA_START_DESTINATION) as Destination?
From 513b4b7d3edbd6ee3a4d10129a5f80856cf7f652 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 23 Jan 2022 01:43:30 +0000
Subject: [PATCH 041/669] Bump coordinatorlayout from 1.1.0 to 1.2.0 (#1756)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 96138883f..95c2178d8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -195,7 +195,7 @@ dependencies {
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.3"
- implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
+ implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
implementation "com.google.android.material:material:1.4.0"
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
From 7a9ba04ff49d988b956ef96c1d54d16693621840 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 23 Jan 2022 02:17:14 +0000
Subject: [PATCH 042/669] Bump material from 1.4.0 to 1.5.0 (#1757)
---
app/build.gradle | 2 +-
.../wulkanowy/ui/modules/main/MainActivity.kt | 19 +++++++------------
2 files changed, 8 insertions(+), 13 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 95c2178d8..b4598ffa2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -196,7 +196,7 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.3"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
- implementation "com.google.android.material:material:1.4.0"
+ implementation "com.google.android.material:material:1.5.0"
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation 'com.github.lopspower:CircularImageView:4.2.0'
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 d81abe345..02af02adc 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
@@ -23,16 +23,7 @@ import io.github.wulkanowy.databinding.ActivityMainBinding
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog
-import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.AppInfo
-import io.github.wulkanowy.utils.InAppReviewHelper
-import io.github.wulkanowy.utils.UpdateHelper
-import io.github.wulkanowy.utils.createNameInitialsDrawable
-import io.github.wulkanowy.utils.dpToPx
-import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.nickOrName
-import io.github.wulkanowy.utils.safelyPopFragments
-import io.github.wulkanowy.utils.setOnViewChangeListener
+import io.github.wulkanowy.utils.*
import timber.log.Timber
import javax.inject.Inject
@@ -169,8 +160,12 @@ class MainActivity : BaseActivity(), MainVie
.setIcon(R.drawable.ic_main_more)
}
selectedItemId = startMenuIndex
- setOnItemSelectedListener { presenter.onTabSelected(it.itemId, false) }
- setOnItemReselectedListener { presenter.onTabSelected(it.itemId, true) }
+ setOnItemSelectedListener {
+ this@MainActivity.presenter.onTabSelected(it.itemId, false)
+ }
+ setOnItemReselectedListener {
+ this@MainActivity.presenter.onTabSelected(it.itemId, true)
+ }
}
}
From e1d82d70ee6abdbca2c99081d1124fd54ed66c64 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sun, 23 Jan 2022 20:04:01 +0100
Subject: [PATCH 043/669] New translations strings.xml (German) (#1746)
---
app/src/main/res/values-de/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 931f643a6..5b2fee80d 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -93,7 +93,7 @@
Vorhergesagte Note
Berechnender Durchschnitt
Wie funktioniert der berechnete Durchschnitt?
- The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n3. Adding calculated averages\n4. Calculating the arithmetic average of summed averages
+ Der berechnete Mittelwert ist das arithmetische Mittel, das aus den Durchschnittswerten der Probanden errechnet wird. Es erlaubt Ihnen, den ungefähre endgültigen Durchschnitt zu kennen. Sie wird auf eine vom Anwender in den Anwendungseinstellungen gewählte Weise berechnet. Es wird empfohlen, die entsprechende Option zu wählen. Das liegt daran, dass die Berechnung der Schuldurchschnitte unterschiedlich ist. Wenn Ihre Schule den Durchschnitt der Fächer auf der Vulcan-Seite angibt, lädt die Anwendung diese Fächer herunter und berechnet nicht den Durchschnitt. Dies kann geändert werden, indem die Berechnung des Durchschnitts in den Anwendungseinstellungen erzwungen wird. \n\nDurchschnitt der Noten nur aus dem ausgewählten Semester :\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in einem bestimmten Semester\n2. Addition der berechneten Durchschnittswerte\n3. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Durchschnitte aus beiden Semestern:\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in Semester 1 und 2\n2. Berechnung des arithmetischen Mittels der berechneten Durchschnitte für Semester 1 und 2 für jedes Fach. \n3. Hinzufügen von berechneten Durchschnittswerten\n4. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Noten aus dem ganzen Jahr:\n1. Berechnung des gewichteten Jahresdurchschnitts für jedes Fach. Der Abschlussdurchschnitt im 1. Semester ist irrelevant. \n3. Addition der berechneten Durchschnittswerte\n4. Berechnung des arithmetischen Mittels der summierten Mittelwerte
Wie funktioniert der endgültige Durchschnitt?
Der Final Average ist das arithmetische Mittel, das aus allen derzeit verfügbaren Abschlussnoten des jeweiligen Semesters berechnet wird. \n\nDas Berechnungsschema besteht aus folgenden Schritten:\n1. Zusammenfassung der von den Lehrern gegebenen Abschlussnoten\n2. Division durch die Anzahl der Fächer, die bereits bewertet wurden
Finaler Durchschnitt
From 009ec433be0476f5984aa7ac2a566a16e326c4b3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 27 Jan 2022 12:06:30 +0000
Subject: [PATCH 044/669] Bump firebase-bom from 29.0.3 to 29.0.4 (#1766)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index b4598ffa2..ed3f964fd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -231,7 +231,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.3.3'
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
- playImplementation platform('com.google.firebase:firebase-bom:29.0.3')
+ playImplementation platform('com.google.firebase:firebase-bom:29.0.4')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 40e093450418da24578e97797664dadf751ba77a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 28 Jan 2022 11:07:22 +0000
Subject: [PATCH 045/669] Bump fuzzywuzzy from 1.3.3 to 1.4.0 (#1765)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index ed3f964fd..affe89298 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -228,7 +228,7 @@ dependencies {
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
implementation "io.coil-kt:coil:1.4.0"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
- implementation 'me.xdrop:fuzzywuzzy:1.3.3'
+ implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
playImplementation platform('com.google.firebase:firebase-bom:29.0.4')
From de9fcb9af95cf85a16cc847f6f0da882a1f84260 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Fri, 28 Jan 2022 12:07:48 +0100
Subject: [PATCH 046/669] Add what-the-stack library (#1767)
---
app/build.gradle | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/build.gradle b/app/build.gradle
index affe89298..498673416 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -246,6 +246,7 @@ dependencies {
debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker"
debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.6'
+ debugImplementation 'com.github.haroldadmin:WhatTheStack:1.0.0-alpha02'
testImplementation "junit:junit:4.13.2"
testImplementation "io.mockk:mockk:$mockk"
From daa7b54dab180ea9be1d5551dda2eb2979cbaf6f Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Fri, 28 Jan 2022 13:43:56 +0100
Subject: [PATCH 047/669] Refactor notification destinations (#1709)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Rafał Borcz
---
.../48.json | 2445 +++++++++++++++++
.../github/wulkanowy/data/db/AppDatabase.kt | 71 +-
.../io/github/wulkanowy/data/db/Converters.kt | 10 +
.../data/db/entities/Notification.kt | 4 +
.../wulkanowy/data/pojos/NotificationData.kt | 6 +-
.../data/serializers/LocalDateSerializer.kt | 32 +
.../services/shortcuts/ShortcutsHelper.kt | 3 +
.../notifications/AppNotificationManager.kt | 8 +-
.../ChangeTimetableNotification.kt | 14 +-
.../NewAttendanceNotification.kt | 4 +-
.../NewConferenceNotification.kt | 4 +-
.../sync/notifications/NewExamNotification.kt | 4 +-
.../notifications/NewGradeNotification.kt | 12 +-
.../notifications/NewHomeworkNotification.kt | 4 +-
.../NewLuckyNumberNotification.kt | 2 +-
.../notifications/NewMessageNotification.kt | 4 +-
.../sync/notifications/NewNoteNotification.kt | 4 +-
.../NewSchoolAnnouncementNotification.kt | 10 +-
.../wulkanowy/ui/modules/Destination.kt | 80 +-
.../debug/notification/mock/timetable.kt | 4 +-
.../NotificationsCenterAdapter.kt | 5 +-
.../NotificationsCenterFragment.kt | 33 +-
.../NotificationsCenterPresenter.kt | 2 +-
.../services/messaging/AppMessagingService.kt | 2 +
24 files changed, 2578 insertions(+), 189 deletions(-)
create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json
create mode 100644 app/src/main/java/io/github/wulkanowy/data/serializers/LocalDateSerializer.kt
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json
new file mode 100644
index 000000000..1c11aae91
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json
@@ -0,0 +1,2445 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 48,
+ "identityHash": "95751b933ad9f835ffc1805f4ef71bdb",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "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"
+ ],
+ "orders": [],
+ "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}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "kindergartenDiaryId",
+ "columnName": "kindergarten_diary_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "kindergarten_diary_id",
+ "semester_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "recipient",
+ "columnName": "recipient_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "removed",
+ "columnName": "removed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+ "fields": [
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "oneDriveId",
+ "columnName": "one_drive_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "real_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ReportingUnits",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shortName",
+ "columnName": "short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderName",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roles",
+ "columnName": "roles",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceId",
+ "columnName": "device_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "destination",
+ "columnName": "destination",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'"
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDismissible",
+ "columnName": "is_dismissible",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "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, '95751b933ad9f835ffc1805f4ef71bdb')"
+ ]
+ }
+}
\ No newline at end of file
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 24bb3917e..379b8738f 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
@@ -1,72 +1,10 @@
package io.github.wulkanowy.data.db
import android.content.Context
-import androidx.room.AutoMigration
-import androidx.room.Database
-import androidx.room.Room
-import androidx.room.RoomDatabase
+import androidx.room.*
import androidx.room.RoomDatabase.JournalMode.TRUNCATE
-import androidx.room.TypeConverters
-import io.github.wulkanowy.data.db.dao.AdminMessageDao
-import io.github.wulkanowy.data.db.dao.AttendanceDao
-import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
-import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
-import io.github.wulkanowy.data.db.dao.ConferenceDao
-import io.github.wulkanowy.data.db.dao.ExamDao
-import io.github.wulkanowy.data.db.dao.GradeDao
-import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
-import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
-import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
-import io.github.wulkanowy.data.db.dao.GradeSummaryDao
-import io.github.wulkanowy.data.db.dao.HomeworkDao
-import io.github.wulkanowy.data.db.dao.LuckyNumberDao
-import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
-import io.github.wulkanowy.data.db.dao.MessagesDao
-import io.github.wulkanowy.data.db.dao.MobileDeviceDao
-import io.github.wulkanowy.data.db.dao.NoteDao
-import io.github.wulkanowy.data.db.dao.NotificationDao
-import io.github.wulkanowy.data.db.dao.RecipientDao
-import io.github.wulkanowy.data.db.dao.ReportingUnitDao
-import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
-import io.github.wulkanowy.data.db.dao.SchoolDao
-import io.github.wulkanowy.data.db.dao.SemesterDao
-import io.github.wulkanowy.data.db.dao.StudentDao
-import io.github.wulkanowy.data.db.dao.StudentInfoDao
-import io.github.wulkanowy.data.db.dao.SubjectDao
-import io.github.wulkanowy.data.db.dao.TeacherDao
-import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
-import io.github.wulkanowy.data.db.dao.TimetableDao
-import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
-import io.github.wulkanowy.data.db.entities.AdminMessage
-import io.github.wulkanowy.data.db.entities.Attendance
-import io.github.wulkanowy.data.db.entities.AttendanceSummary
-import io.github.wulkanowy.data.db.entities.CompletedLesson
-import io.github.wulkanowy.data.db.entities.Conference
-import io.github.wulkanowy.data.db.entities.Exam
-import io.github.wulkanowy.data.db.entities.Grade
-import io.github.wulkanowy.data.db.entities.GradePartialStatistics
-import io.github.wulkanowy.data.db.entities.GradePointsStatistics
-import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
-import io.github.wulkanowy.data.db.entities.GradeSummary
-import io.github.wulkanowy.data.db.entities.Homework
-import io.github.wulkanowy.data.db.entities.LuckyNumber
-import io.github.wulkanowy.data.db.entities.Message
-import io.github.wulkanowy.data.db.entities.MessageAttachment
-import io.github.wulkanowy.data.db.entities.MobileDevice
-import io.github.wulkanowy.data.db.entities.Note
-import io.github.wulkanowy.data.db.entities.Notification
-import io.github.wulkanowy.data.db.entities.Recipient
-import io.github.wulkanowy.data.db.entities.ReportingUnit
-import io.github.wulkanowy.data.db.entities.School
-import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
-import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.db.entities.StudentInfo
-import io.github.wulkanowy.data.db.entities.Subject
-import io.github.wulkanowy.data.db.entities.Teacher
-import io.github.wulkanowy.data.db.entities.Timetable
-import io.github.wulkanowy.data.db.entities.TimetableAdditional
-import io.github.wulkanowy.data.db.entities.TimetableHeader
+import io.github.wulkanowy.data.db.dao.*
+import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.db.migrations.*
import io.github.wulkanowy.utils.AppInfo
import javax.inject.Singleton
@@ -108,6 +46,7 @@ import javax.inject.Singleton
autoMigrations = [
AutoMigration(from = 44, to = 45),
AutoMigration(from = 46, to = 47),
+ AutoMigration(from = 47, to = 48),
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@@ -116,7 +55,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
- const val VERSION_SCHEMA = 47
+ const val VERSION_SCHEMA = 48
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt
index b7013a32a..9d3beae1f 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/Converters.kt
@@ -1,11 +1,14 @@
package io.github.wulkanowy.data.db
import androidx.room.TypeConverter
+import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.utils.toTimestamp
import kotlinx.serialization.SerializationException
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
+import java.time.*
+import java.util.*
import java.time.Instant
import java.time.LocalDate
import java.time.Month
@@ -58,4 +61,11 @@ class Converters {
emptyList() // handle errors from old gson Pair serialized data
}
}
+
+ @TypeConverter
+ fun destinationToString(destination: Destination) = json.encodeToString(destination)
+
+ @TypeConverter
+ fun stringToDestination(destination: String): Destination = json.decodeFromString(destination)
+
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt
index 4867e3329..c3267f24e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Notification.kt
@@ -4,6 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import io.github.wulkanowy.services.sync.notifications.NotificationType
+import io.github.wulkanowy.ui.modules.Destination
import java.time.Instant
@Entity(tableName = "Notifications")
@@ -18,6 +19,9 @@ data class Notification(
val type: NotificationType,
+ @ColumnInfo(defaultValue = "{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}")
+ val destination: Destination,
+
val date: Instant,
val data: String? = null
diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/NotificationData.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/NotificationData.kt
index 0748ba647..f4fd0fc8a 100644
--- a/app/src/main/java/io/github/wulkanowy/data/pojos/NotificationData.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/pojos/NotificationData.kt
@@ -1,10 +1,10 @@
package io.github.wulkanowy.data.pojos
-import android.content.Intent
import io.github.wulkanowy.services.sync.notifications.NotificationType
+import io.github.wulkanowy.ui.modules.Destination
data class NotificationData(
- val intentToStart: Intent,
+ val destination: Destination,
val title: String,
val content: String
)
@@ -13,7 +13,7 @@ data class GroupNotificationData(
val notificationDataList: List,
val title: String,
val content: String,
- val intentToStart: Intent,
+ val destination: Destination,
val type: NotificationType
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/serializers/LocalDateSerializer.kt b/app/src/main/java/io/github/wulkanowy/data/serializers/LocalDateSerializer.kt
new file mode 100644
index 000000000..ba97d37a2
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/serializers/LocalDateSerializer.kt
@@ -0,0 +1,32 @@
+package io.github.wulkanowy.data.serializers
+
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.KSerializer
+import kotlinx.serialization.descriptors.PrimitiveKind
+import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
+import kotlinx.serialization.descriptors.nullable
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import java.time.LocalDate
+
+@OptIn(ExperimentalSerializationApi::class)
+object LocalDateSerializer : KSerializer {
+
+ override val descriptor = PrimitiveSerialDescriptor("LocalDate", PrimitiveKind.LONG).nullable
+
+ override fun serialize(encoder: Encoder, value: LocalDate?) {
+ if (value == null) {
+ encoder.encodeNull()
+ } else {
+ encoder.encodeNotNullMark()
+ encoder.encodeLong(value.toEpochDay())
+ }
+ }
+
+ override fun deserialize(decoder: Decoder): LocalDate? =
+ if (decoder.decodeNotNullMark()) {
+ LocalDate.ofEpochDay(decoder.decodeLong())
+ } else {
+ decoder.decodeNull()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt b/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt
index 4ad9ac120..ee31af46b 100644
--- a/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt
@@ -15,6 +15,9 @@ import javax.inject.Singleton
@Singleton
class ShortcutsHelper @Inject constructor(@ApplicationContext private val context: Context) {
+ // Destination cannot be used here as shortcuts
+ // require their intents to only use primitive types (see PersistableBundle.isValidType).
+
private val destinations = mapOf(
"grade" to Destination.Grade,
"attendance" to Destination.Attendance,
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt
index 7ac532aeb..dadb68c50 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/AppNotificationManager.kt
@@ -14,6 +14,7 @@ import io.github.wulkanowy.data.pojos.GroupNotificationData
import io.github.wulkanowy.data.pojos.NotificationData
import io.github.wulkanowy.data.repositories.NotificationRepository
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
@@ -47,7 +48,7 @@ class AppNotificationManager @Inject constructor(
PendingIntent.getActivity(
context,
Random.nextInt(),
- notificationData.intentToStart,
+ SplashActivity.getStartIntent(context, notificationData.destination),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
)
)
@@ -92,7 +93,7 @@ class AppNotificationManager @Inject constructor(
PendingIntent.getActivity(
context,
Random.nextInt(),
- notificationData.intentToStart,
+ SplashActivity.getStartIntent(context, notificationData.destination),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
)
)
@@ -146,7 +147,7 @@ class AppNotificationManager @Inject constructor(
PendingIntent.getActivity(
context,
Random.nextInt(),
- groupNotificationData.intentToStart,
+ SplashActivity.getStartIntent(context, groupNotificationData.destination),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
)
)
@@ -168,6 +169,7 @@ class AppNotificationManager @Inject constructor(
studentId = student.id,
title = notificationData.title,
content = notificationData.content,
+ destination = notificationData.destination,
type = notificationType,
date = Instant.now(),
)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt
index b1f9a7b06..43ae1fea9 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/ChangeTimetableNotification.kt
@@ -8,7 +8,6 @@ import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.pojos.GroupNotificationData
import io.github.wulkanowy.data.pojos.NotificationData
import io.github.wulkanowy.ui.modules.Destination
-import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.getPlural
import io.github.wulkanowy.utils.toFormattedString
import java.time.Instant
@@ -23,8 +22,9 @@ class ChangeTimetableNotification @Inject constructor(
suspend fun notify(items: List, student: Student) {
val currentTime = Instant.now()
val changedLessons = items.filter { (it.canceled || it.changes) && it.start > currentTime }
- val notificationDataList = changedLessons.groupBy { it.date }
- .map { (date, lessons) ->
+ val lessonsByDate = changedLessons.groupBy { it.date }
+ val notificationDataList = lessonsByDate
+ .flatMap { (date, lessons) ->
getNotificationContents(date, lessons).map {
NotificationData(
title = context.getPlural(
@@ -32,14 +32,10 @@ class ChangeTimetableNotification @Inject constructor(
1
),
content = it,
- intentToStart = SplashActivity.getStartIntent(
- context = context,
- destination = Destination.Timetable(date)
- )
+ destination = Destination.Timetable(date)
)
}
}
- .flatten()
.ifEmpty { return }
val groupNotificationData = GroupNotificationData(
@@ -53,7 +49,7 @@ class ChangeTimetableNotification @Inject constructor(
changedLessons.size,
changedLessons.size
),
- intentToStart = SplashActivity.getStartIntent(context, Destination.Timetable()),
+ destination = Destination.Timetable(lessonsByDate.toSortedMap().firstKey()),
type = NotificationType.CHANGE_TIMETABLE
)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt
index c78dcd053..49842c9a6 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt
@@ -31,7 +31,7 @@ class NewAttendanceNotification @Inject constructor(
NotificationData(
title = context.getPlural(R.plurals.attendance_notify_new_items_title, 1),
content = it,
- intentToStart = SplashActivity.getStartIntent(context, Destination.Attendance)
+ destination = Destination.Attendance
)
}
@@ -46,7 +46,7 @@ class NewAttendanceNotification @Inject constructor(
notificationDataList.size,
notificationDataList.size
),
- intentToStart = SplashActivity.getStartIntent(context, Destination.Attendance),
+ destination = Destination.Attendance,
type = NotificationType.NEW_ATTENDANCE
)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt
index d27c57285..92977ebb1 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewConferenceNotification.kt
@@ -31,7 +31,7 @@ class NewConferenceNotification @Inject constructor(
NotificationData(
title = context.getPlural(R.plurals.conference_notify_new_item_title, 1),
content = it,
- intentToStart = SplashActivity.getStartIntent(context, Destination.Conference)
+ destination = Destination.Conference
)
}
@@ -43,7 +43,7 @@ class NewConferenceNotification @Inject constructor(
lines.size,
lines.size
),
- intentToStart = SplashActivity.getStartIntent(context, Destination.Conference),
+ destination = Destination.Conference,
type = NotificationType.NEW_CONFERENCE
)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewExamNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewExamNotification.kt
index b3cf04c41..125bbf92d 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewExamNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewExamNotification.kt
@@ -31,7 +31,7 @@ class NewExamNotification @Inject constructor(
NotificationData(
title = context.getPlural(R.plurals.exam_notify_new_item_title, 1),
content = it,
- intentToStart = SplashActivity.getStartIntent(context, Destination.Exam),
+ destination = Destination.Exam,
)
}
@@ -43,7 +43,7 @@ class NewExamNotification @Inject constructor(
lines.size,
lines.size
),
- intentToStart = SplashActivity.getStartIntent(context, Destination.Exam),
+ destination = Destination.Exam,
type = NotificationType.NEW_EXAM
)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt
index 32d2ba6a0..9b49ed178 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewGradeNotification.kt
@@ -26,7 +26,7 @@ class NewGradeNotification @Inject constructor(
append("${it.subject}: ${it.entry}")
if (it.comment.isNotBlank()) append(" (${it.comment})")
},
- intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
+ destination = Destination.Grade,
)
}
@@ -34,7 +34,7 @@ class NewGradeNotification @Inject constructor(
notificationDataList = notificationDataList,
title = context.getPlural(R.plurals.grade_new_items, items.size),
content = context.getPlural(R.plurals.grade_notify_new_items, items.size, items.size),
- intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
+ destination = Destination.Grade,
type = NotificationType.NEW_GRADE_DETAILS
)
@@ -46,7 +46,7 @@ class NewGradeNotification @Inject constructor(
NotificationData(
title = context.getPlural(R.plurals.grade_new_items_predicted, 1),
content = "${it.subject}: ${it.predictedGrade}",
- intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
+ destination = Destination.Grade,
)
}
@@ -58,7 +58,7 @@ class NewGradeNotification @Inject constructor(
items.size,
items.size
),
- intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
+ destination = Destination.Grade,
type = NotificationType.NEW_GRADE_PREDICTED
)
@@ -70,7 +70,7 @@ class NewGradeNotification @Inject constructor(
NotificationData(
title = context.getPlural(R.plurals.grade_new_items_final, 1),
content = "${it.subject}: ${it.finalGrade}",
- intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
+ destination = Destination.Grade,
)
}
@@ -82,7 +82,7 @@ class NewGradeNotification @Inject constructor(
items.size,
items.size
),
- intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
+ destination = Destination.Grade,
type = NotificationType.NEW_GRADE_FINAL
)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewHomeworkNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewHomeworkNotification.kt
index ff32aa66f..856c51581 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewHomeworkNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewHomeworkNotification.kt
@@ -31,7 +31,7 @@ class NewHomeworkNotification @Inject constructor(
NotificationData(
title = context.getPlural(R.plurals.homework_notify_new_item_title, 1),
content = it,
- intentToStart = SplashActivity.getStartIntent(context, Destination.Homework),
+ destination = Destination.Homework,
)
}
@@ -42,7 +42,7 @@ class NewHomeworkNotification @Inject constructor(
lines.size,
lines.size
),
- intentToStart = SplashActivity.getStartIntent(context, Destination.Homework),
+ destination = Destination.Homework,
type = NotificationType.NEW_HOMEWORK,
notificationDataList = notificationDataList
)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewLuckyNumberNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewLuckyNumberNotification.kt
index 5c36a06c0..bbe9b8a18 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewLuckyNumberNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewLuckyNumberNotification.kt
@@ -22,7 +22,7 @@ class NewLuckyNumberNotification @Inject constructor(
R.string.lucky_number_notify_new_item,
item.luckyNumber.toString()
),
- intentToStart = SplashActivity.getStartIntent(context, Destination.LuckyNumber)
+ destination = Destination.LuckyNumber
)
appNotificationManager.sendSingleNotification(
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt
index b98d34668..5c3c52c5b 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt
@@ -22,7 +22,7 @@ class NewMessageNotification @Inject constructor(
NotificationData(
title = context.getPlural(R.plurals.message_new_items, 1),
content = "${it.sender}: ${it.subject}",
- intentToStart = SplashActivity.getStartIntent(context, Destination.Message),
+ destination = Destination.Message,
)
}
@@ -30,7 +30,7 @@ class NewMessageNotification @Inject constructor(
notificationDataList = notificationDataList,
title = context.getPlural(R.plurals.message_new_items, items.size),
content = context.getPlural(R.plurals.message_notify_new_items, items.size, items.size),
- intentToStart = SplashActivity.getStartIntent(context, Destination.Message),
+ destination = Destination.Message,
type = NotificationType.NEW_MESSAGE
)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewNoteNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewNoteNotification.kt
index 65520e01b..dae7d4330 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewNoteNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewNoteNotification.kt
@@ -29,13 +29,13 @@ class NewNoteNotification @Inject constructor(
NotificationData(
title = context.getPlural(titleRes, 1),
content = "${it.teacher}: ${it.category}",
- intentToStart = SplashActivity.getStartIntent(context, Destination.Note),
+ destination = Destination.Note,
)
}
val groupNotificationData = GroupNotificationData(
notificationDataList = notificationDataList,
- intentToStart = SplashActivity.getStartIntent(context, Destination.Note),
+ destination = Destination.Note,
title = context.getPlural(R.plurals.note_new_items, items.size),
content = context.getPlural(R.plurals.note_notify_new_items, items.size, items.size),
type = NotificationType.NEW_NOTE
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt
index 695438a70..cc7e46564 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewSchoolAnnouncementNotification.kt
@@ -21,10 +21,7 @@ class NewSchoolAnnouncementNotification @Inject constructor(
suspend fun notify(items: List, student: Student) {
val notificationDataList = items.map {
NotificationData(
- intentToStart = SplashActivity.getStartIntent(
- context = context,
- destination = Destination.SchoolAnnouncement
- ),
+ destination = Destination.SchoolAnnouncement,
title = context.getPlural(
R.plurals.school_announcement_notify_new_item_title,
1
@@ -34,10 +31,7 @@ class NewSchoolAnnouncementNotification @Inject constructor(
}
val groupNotificationData = GroupNotificationData(
type = NotificationType.NEW_ANNOUNCEMENT,
- intentToStart = SplashActivity.getStartIntent(
- context = context,
- destination = Destination.SchoolAnnouncement
- ),
+ destination = Destination.SchoolAnnouncement,
title = context.getPlural(
R.plurals.school_announcement_notify_new_item_title,
items.size
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
index 43d4b5f9f..f49c48891 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
@@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules
import androidx.fragment.app.Fragment
+import io.github.wulkanowy.data.serializers.LocalDateSerializer
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
import io.github.wulkanowy.ui.modules.dashboard.DashboardFragment
@@ -14,18 +15,19 @@ import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
-import java.io.Serializable
+import kotlinx.serialization.Serializable
import java.time.LocalDate
-sealed interface Destination : Serializable {
+@Serializable
+sealed class Destination private constructor() : java.io.Serializable {
/*
Type in children classes have to be as getter to avoid null in enums
https://stackoverflow.com/questions/68866453/kotlin-enum-val-is-returning-null-despite-being-set-at-compile-time
*/
- val type: Type
+ abstract val type: Type
- val fragment: Fragment
+ abstract val fragment: Fragment
enum class Type(val defaultDestination: Destination) {
DASHBOARD(Dashboard),
@@ -43,94 +45,84 @@ sealed interface Destination : Serializable {
MESSAGE(Message);
}
- object Dashboard : Destination {
-
+ @Serializable
+ object Dashboard : Destination() {
override val type get() = Type.DASHBOARD
-
override val fragment get() = DashboardFragment.newInstance()
}
- object Grade : Destination {
-
+ @Serializable
+ object Grade : Destination() {
override val type get() = Type.GRADE
-
override val fragment get() = GradeFragment.newInstance()
}
- object Attendance : Destination {
-
+ @Serializable
+ object Attendance : Destination() {
override val type get() = Type.ATTENDANCE
-
override val fragment get() = AttendanceFragment.newInstance()
}
- object Exam : Destination {
-
+ @Serializable
+ object Exam : Destination() {
override val type get() = Type.EXAM
-
override val fragment get() = ExamFragment.newInstance()
}
- data class Timetable(val date: LocalDate? = null) : Destination {
-
+ @Serializable
+ data class Timetable(
+ @Serializable(with = LocalDateSerializer::class)
+ private val date: LocalDate? = null
+ ) : Destination() {
override val type get() = Type.TIMETABLE
-
override val fragment get() = TimetableFragment.newInstance(date)
}
- object Homework : Destination {
-
+ @Serializable
+ object Homework : Destination() {
override val type get() = Type.HOMEWORK
-
override val fragment get() = HomeworkFragment.newInstance()
}
- object Note : Destination {
-
+ @Serializable
+ object Note : Destination() {
override val type get() = Type.NOTE
-
override val fragment get() = NoteFragment.newInstance()
}
- object Conference : Destination {
-
+ @Serializable
+ object Conference : Destination() {
override val type get() = Type.CONFERENCE
-
override val fragment get() = ConferenceFragment.newInstance()
}
- object SchoolAnnouncement : Destination {
-
+ @Serializable
+ object SchoolAnnouncement : Destination() {
override val type get() = Type.SCHOOL_ANNOUNCEMENT
-
override val fragment get() = SchoolAnnouncementFragment.newInstance()
}
- object School : Destination {
-
+ @Serializable
+ object School : Destination() {
override val type get() = Type.SCHOOL
-
override val fragment get() = SchoolFragment.newInstance()
}
- object LuckyNumber : Destination {
-
+ @Serializable
+ object LuckyNumber : Destination() {
override val type get() = Type.LUCKY_NUMBER
-
override val fragment get() = LuckyNumberFragment.newInstance()
}
- object More : Destination {
-
+ @Serializable
+ object More : Destination() {
override val type get() = Type.MORE
-
override val fragment get() = MoreFragment.newInstance()
}
- object Message : Destination {
-
+ @Serializable
+ object Message : Destination() {
override val type get() = Type.MESSAGE
-
override val fragment get() = MessageFragment.newInstance()
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt
index bb8a8df36..ff968654d 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/timetable.kt
@@ -25,8 +25,8 @@ private fun generateTimetable(subject: String, room: String, roomOld: String) =
diaryId = 0,
date = LocalDate.now().minusDays(Random.nextLong(0, 8)),
number = 1,
- start = Instant.now(),
- end = Instant.now().plus(Duration.ofHours(1)),
+ start = Instant.now().plus(Duration.ofHours(1)),
+ end = Instant.now(),
subjectOld = "",
group = "",
room = room,
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterAdapter.kt
index 27b3637ad..92c54f45c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterAdapter.kt
@@ -7,14 +7,13 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.data.db.entities.Notification
import io.github.wulkanowy.databinding.ItemNotificationsCenterBinding
-import io.github.wulkanowy.services.sync.notifications.NotificationType
import io.github.wulkanowy.utils.toFormattedString
import javax.inject.Inject
class NotificationsCenterAdapter @Inject constructor() :
ListAdapter(DiffUtilCallback()) {
- var onItemClickListener: (NotificationType) -> Unit = {}
+ var onItemClickListener: (Notification) -> Unit = {}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
ItemNotificationsCenterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
@@ -29,7 +28,7 @@ class NotificationsCenterAdapter @Inject constructor() :
notificationsCenterItemDate.text = item.date.toFormattedString("HH:mm, d MMM")
notificationsCenterItemIcon.setImageResource(item.type.icon)
- root.setOnClickListener { onItemClickListener(item.type) }
+ root.setOnClickListener { onItemClickListener(item) }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt
index f3bbc42de..4f1943f48 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt
@@ -3,26 +3,14 @@ package io.github.wulkanowy.ui.modules.notificationscenter
import android.os.Bundle
import android.view.View
import androidx.core.view.isVisible
-import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Notification
import io.github.wulkanowy.databinding.FragmentNotificationsCenterBinding
-import io.github.wulkanowy.services.sync.notifications.NotificationType
import io.github.wulkanowy.ui.base.BaseFragment
-import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
-import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
-import io.github.wulkanowy.ui.modules.exam.ExamFragment
-import io.github.wulkanowy.ui.modules.grade.GradeFragment
-import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
-import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
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.note.NoteFragment
-import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
-import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
import javax.inject.Inject
@AndroidEntryPoint
@@ -54,9 +42,8 @@ class NotificationsCenterFragment :
}
override fun initView() {
- notificationsCenterAdapter.onItemClickListener = { notificationType ->
- notificationType.toDestinationFragment()
- ?.let { (requireActivity() as MainActivity).pushView(it) }
+ notificationsCenterAdapter.onItemClickListener = { notification ->
+ (requireActivity() as MainActivity).pushView(notification.destination.fragment)
}
with(binding.notificationsCenterRecycler) {
@@ -93,20 +80,4 @@ class NotificationsCenterFragment :
presenter.onDetachView()
super.onDestroyView()
}
-
- private fun NotificationType.toDestinationFragment(): Fragment? = when (this) {
- NotificationType.NEW_CONFERENCE -> ConferenceFragment.newInstance()
- NotificationType.NEW_EXAM -> ExamFragment.newInstance()
- NotificationType.NEW_GRADE_DETAILS -> GradeFragment.newInstance()
- NotificationType.NEW_GRADE_PREDICTED -> GradeFragment.newInstance()
- NotificationType.NEW_GRADE_FINAL -> GradeFragment.newInstance()
- NotificationType.NEW_HOMEWORK -> HomeworkFragment.newInstance()
- NotificationType.NEW_LUCKY_NUMBER -> LuckyNumberFragment.newInstance()
- NotificationType.NEW_MESSAGE -> MessageFragment.newInstance()
- NotificationType.NEW_NOTE -> NoteFragment.newInstance()
- NotificationType.NEW_ANNOUNCEMENT -> SchoolAnnouncementFragment.newInstance()
- NotificationType.PUSH -> null
- NotificationType.CHANGE_TIMETABLE -> TimetableFragment.newInstance()
- NotificationType.NEW_ATTENDANCE -> AttendanceFragment.newInstance()
- }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterPresenter.kt
index 394c23101..de42e5678 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterPresenter.kt
@@ -48,7 +48,7 @@ class NotificationsCenterPresenter @Inject constructor(
emitAll(notificationRepository.getNotifications(studentId))
}
.map { notificationList -> notificationList.sortedByDescending { it.date } }
- .catch { Timber.i("Loading notifications result: An exception occurred") }
+ .catch { Timber.i("Loading notifications result: An exception occurred: `$it`") }
.onEach {
Timber.i("Loading notifications result: Success")
diff --git a/app/src/play/java/io/github/wulkanowy/services/messaging/AppMessagingService.kt b/app/src/play/java/io/github/wulkanowy/services/messaging/AppMessagingService.kt
index d9a3780ba..a614c65dc 100644
--- a/app/src/play/java/io/github/wulkanowy/services/messaging/AppMessagingService.kt
+++ b/app/src/play/java/io/github/wulkanowy/services/messaging/AppMessagingService.kt
@@ -7,6 +7,7 @@ import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.entities.Notification
import io.github.wulkanowy.data.repositories.NotificationRepository
import io.github.wulkanowy.services.sync.notifications.NotificationType
+import io.github.wulkanowy.ui.modules.Destination
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -38,6 +39,7 @@ class AppMessagingService : FirebaseMessagingService() {
data = customData,
date = Instant.now(),
type = NotificationType.PUSH,
+ destination = Destination.Dashboard,
studentId = -1
)
From d07b0dbc986aeed39718092fe77f335637ad54c9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 29 Jan 2022 04:46:09 +0000
Subject: [PATCH 048/669] Bump WhatTheStack from 1.0.0-alpha02 to 1.0.0-alpha03
(#1768)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 498673416..e4c4034b3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -246,7 +246,7 @@ dependencies {
debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker"
debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.6'
- debugImplementation 'com.github.haroldadmin:WhatTheStack:1.0.0-alpha02'
+ debugImplementation 'com.github.haroldadmin:WhatTheStack:1.0.0-alpha03'
testImplementation "junit:junit:4.13.2"
testImplementation "io.mockk:mockk:$mockk"
From 0b0993be9aaa41616427df28804e827b66056647 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 29 Jan 2022 04:47:00 +0000
Subject: [PATCH 049/669] Bump agcp from 1.6.3.300 to 1.6.4.200 (#1772)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 6c0e709d2..42dcc6921 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.0.4'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
- classpath 'com.huawei.agconnect:agcp:1.6.3.300'
+ classpath 'com.huawei.agconnect:agcp:1.6.4.200'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.1"
From cfcc051ce40a00c22758a12affa9f432147a96b7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 29 Jan 2022 04:47:26 +0000
Subject: [PATCH 050/669] Bump gradle from 7.0.4 to 7.1.0 (#1769)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 42dcc6921..0da00b390 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
- classpath 'com.android.tools.build:gradle:7.0.4'
+ classpath 'com.android.tools.build:gradle:7.1.0'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.huawei.agconnect:agcp:1.6.4.200'
From 01b8bd9d4a8f7fc7c030ab0ab8d636288ba466f4 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 29 Jan 2022 04:47:56 +0000
Subject: [PATCH 051/669] Bump agconnect-crash from 1.6.3.300 to 1.6.4.200
(#1770)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index e4c4034b3..3f7b95151 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -240,7 +240,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:20.5.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.3.2.300'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.3.300'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.4.200'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From a4f455b38fd06d85096e1cb69db49e871a199710 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 29 Jan 2022 04:55:02 +0000
Subject: [PATCH 052/669] Bump hianalytics from 6.3.2.300 to 6.4.0.300 (#1771)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 3f7b95151..1a0e00ae5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -239,7 +239,7 @@ dependencies {
playImplementation 'com.google.android.play:core-ktx:1.8.1'
playImplementation 'com.google.android.gms:play-services-ads:20.5.0'
- hmsImplementation 'com.huawei.hms:hianalytics:6.3.2.300'
+ hmsImplementation 'com.huawei.hms:hianalytics:6.4.0.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.4.200'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From ce36e86bb2b1bfcd488ebefb541150932c53072c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 29 Jan 2022 05:38:55 +0000
Subject: [PATCH 053/669] Bump preference-ktx from 1.1.1 to 1.2.0 (#1773)
---
app/build.gradle | 2 +-
.../github/wulkanowy/ui/modules/main/MainActivity.kt | 6 ++++--
.../ui/modules/settings/advanced/AdvancedFragment.kt | 4 ++--
.../modules/settings/appearance/AppearanceFragment.kt | 4 ++--
.../settings/notifications/NotificationsFragment.kt | 10 +++++-----
.../wulkanowy/ui/modules/settings/sync/SyncFragment.kt | 4 ++--
6 files changed, 16 insertions(+), 14 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 1a0e00ae5..a31b6b1fb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -190,7 +190,7 @@ dependencies {
implementation "androidx.fragment:fragment-ktx:1.4.0"
implementation "androidx.annotation:annotation:1.3.0"
- implementation "androidx.preference:preference-ktx:1.1.1"
+ implementation "androidx.preference:preference-ktx:1.2.0"
implementation "androidx.recyclerview:recyclerview:1.2.1"
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
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 02af02adc..0cd38ac7d 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
@@ -173,8 +173,10 @@ class MainActivity : BaseActivity(), MainVie
caller: PreferenceFragmentCompat,
pref: Preference
): Boolean {
- val fragment =
- supportFragmentManager.fragmentFactory.instantiate(classLoader, pref.fragment)
+ val fragment = supportFragmentManager.fragmentFactory.instantiate(
+ classLoader,
+ pref.fragment.toString()
+ )
pushView(fragment)
return true
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt
index bef726cad..b4ba5bc4b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/advanced/AdvancedFragment.kt
@@ -64,11 +64,11 @@ class AdvancedFragment : PreferenceFragmentCompat(),
override fun onResume() {
super.onResume()
- preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
+ preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
- preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
+ preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt
index f603de781..1f6d5143b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt
@@ -80,11 +80,11 @@ class AppearanceFragment : PreferenceFragmentCompat(),
override fun onResume() {
super.onResume()
- preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
+ preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
- preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
+ preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt
index 84fee7179..364ad2137 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/notifications/NotificationsFragment.kt
@@ -83,10 +83,10 @@ class NotificationsFragment : PreferenceFragmentCompat(),
}
override fun onCreateRecyclerView(
- inflater: LayoutInflater?,
- parent: ViewGroup?,
+ inflater: LayoutInflater,
+ parent: ViewGroup,
state: Bundle?
- ): RecyclerView? = super.onCreateRecyclerView(inflater, parent, state)
+ ): RecyclerView = super.onCreateRecyclerView(inflater, parent, state)
.also {
it.itemAnimator = null
it.layoutAnimation = null
@@ -214,11 +214,11 @@ class NotificationsFragment : PreferenceFragmentCompat(),
override fun onResume() {
super.onResume()
- preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
+ preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
- preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
+ preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt
index d81c35d39..8477e3222 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/sync/SyncFragment.kt
@@ -96,11 +96,11 @@ class SyncFragment : PreferenceFragmentCompat(),
override fun onResume() {
super.onResume()
- preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
+ preferenceScreen.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
}
override fun onPause() {
super.onPause()
- preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this)
+ preferenceScreen.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
}
}
From 923af85d18bdec1b3dd14173e131687d1242c3b7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 2 Feb 2022 01:53:16 +0000
Subject: [PATCH 054/669] Bump fragment-ktx from 1.4.0 to 1.4.1 (#1774)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index a31b6b1fb..92f76303c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -187,7 +187,7 @@ dependencies {
implementation 'androidx.core:core-splashscreen:1.0.0-beta01'
implementation "androidx.activity:activity-ktx:1.4.0"
implementation "androidx.appcompat:appcompat:1.4.1"
- implementation "androidx.fragment:fragment-ktx:1.4.0"
+ implementation "androidx.fragment:fragment-ktx:1.4.1"
implementation "androidx.annotation:annotation:1.3.0"
implementation "androidx.preference:preference-ktx:1.2.0"
From 96ee4bd9e5d5d516f1af9a90c2884c10fcdde912 Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Wed, 2 Feb 2022 03:44:14 +0100
Subject: [PATCH 055/669] Keep reacting to live changes in dashboard after a
force refresh (#1594)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Rafał Borcz
---
.../modules/dashboard/DashboardPresenter.kt | 37 +++++++++++++------
1 file changed, 26 insertions(+), 11 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index a1845ab59..cad8d112b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -104,7 +104,7 @@ class DashboardPresenter @Inject constructor(
forceRefresh: Boolean
) = dashboardTilesToLoad.filter { newItemToLoad ->
dashboardLoadedTiles.none { it == newItemToLoad } || forceRefresh
- || newItemToLoad == DashboardItem.Tile.ADMIN_MESSAGE
+ || newItemToLoad == DashboardItem.Tile.ADMIN_MESSAGE
}
private fun removeUnselectedTiles(tilesToLoad: List) {
@@ -254,7 +254,8 @@ class DashboardPresenter @Inject constructor(
attendanceFlow
) { luckyNumberResource, messageResource, attendanceResource ->
val error =
- luckyNumberResource?.error ?: messageResource?.error ?: attendanceResource?.error
+ luckyNumberResource?.error ?: messageResource?.error
+ ?: attendanceResource?.error
error?.let { throw it }
val luckyNumber = luckyNumberResource?.data?.luckyNumber
@@ -295,7 +296,7 @@ class DashboardPresenter @Inject constructor(
)
errorHandler.dispatch(it)
}
- .launch("horizontal_group")
+ .launch("horizontal_group ${if (forceRefresh) "-forceRefresh" else ""}")
}
private fun loadGrades(student: Student, forceRefresh: Boolean) {
@@ -356,7 +357,7 @@ class DashboardPresenter @Inject constructor(
updateData(DashboardItem.Grades(error = it.error), forceRefresh)
}
}
- }.launch("dashboard_grades")
+ }.launchWithUniqueRefreshJob("dashboard_grades", forceRefresh)
}
private fun loadLessons(student: Student, forceRefresh: Boolean) {
@@ -400,7 +401,7 @@ class DashboardPresenter @Inject constructor(
)
}
}
- }.launch("dashboard_lessons")
+ }.launchWithUniqueRefreshJob("dashboard_lessons", forceRefresh)
}
private fun loadHomework(student: Student, forceRefresh: Boolean) {
@@ -447,7 +448,7 @@ class DashboardPresenter @Inject constructor(
updateData(DashboardItem.Homework(error = it.error), forceRefresh)
}
}
- }.launch("dashboard_homework")
+ }.launchWithUniqueRefreshJob("dashboard_homework", forceRefresh)
}
private fun loadSchoolAnnouncements(student: Student, forceRefresh: Boolean) {
@@ -477,7 +478,7 @@ class DashboardPresenter @Inject constructor(
updateData(DashboardItem.Announcements(error = it.error), forceRefresh)
}
}
- }.launch("dashboard_announcements")
+ }.launchWithUniqueRefreshJob("dashboard_announcements", forceRefresh)
}
private fun loadExams(student: Student, forceRefresh: Boolean) {
@@ -521,7 +522,7 @@ class DashboardPresenter @Inject constructor(
updateData(DashboardItem.Exams(error = it.error), forceRefresh)
}
}
- }.launch("dashboard_exams")
+ }.launchWithUniqueRefreshJob("dashboard_exams", forceRefresh)
}
private fun loadConferences(student: Student, forceRefresh: Boolean) {
@@ -558,7 +559,7 @@ class DashboardPresenter @Inject constructor(
updateData(DashboardItem.Conferences(error = it.error), forceRefresh)
}
}
- }.launch("dashboard_conferences")
+ }.launchWithUniqueRefreshJob("dashboard_conferences", forceRefresh)
}
private fun loadAdminMessage(student: Student, forceRefresh: Boolean) {
@@ -594,7 +595,7 @@ class DashboardPresenter @Inject constructor(
}
}
}
- .launch("dashboard_admin_messages")
+ .launchWithUniqueRefreshJob("dashboard_admin_messages", forceRefresh)
}
private fun updateData(dashboardItem: DashboardItem, forceRefresh: Boolean) {
@@ -733,4 +734,18 @@ class DashboardPresenter @Inject constructor(
dashboardItemsPosition?.getOrDefault(tile.type, defaultPosition) ?: tile.type.ordinal
}
}
-}
\ No newline at end of file
+
+ private fun Flow>.launchWithUniqueRefreshJob(name: String, forceRefresh: Boolean) {
+ val jobName = if (forceRefresh) "$name-forceRefresh" else name
+
+ if (forceRefresh) {
+ onEach {
+ if (it.status == Status.SUCCESS) {
+ cancelJobs(jobName)
+ }
+ }.launch(jobName)
+ } else {
+ launch(jobName)
+ }
+ }
+}
From be046a1ddd879dfe7a7eacb46a93011c6bf72878 Mon Sep 17 00:00:00 2001
From: Patryk <43276401+Zaptyp@users.noreply.github.com>
Date: Mon, 7 Feb 2022 03:16:17 +0100
Subject: [PATCH 056/669] Update date in LICENSE file (#1775)
---
LICENSE | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/LICENSE b/LICENSE
index 2fb96cee8..c97032f74 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2021 Wulkanowy
+ Copyright 2022 Wulkanowy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
From 6290663f02834f5866e1108bce745ef45b1aca69 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 7 Feb 2022 04:41:09 +0000
Subject: [PATCH 057/669] Bump gradle from 7.1.0 to 7.1.1 (#1777)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 0da00b390..ab598e633 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
- classpath 'com.android.tools.build:gradle:7.1.0'
+ classpath 'com.android.tools.build:gradle:7.1.1'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.huawei.agconnect:agcp:1.6.4.200'
From 84d0ba525f440f7310aacb09e8f551716ee97c41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Thu, 10 Feb 2022 07:36:44 +0100
Subject: [PATCH 058/669] Fix blank description in exam details (#1778)
---
.../io/github/wulkanowy/ui/modules/exam/ExamDialog.kt | 5 ++++-
.../ui/modules/grade/details/GradeDetailsDialog.kt | 10 ++--------
2 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
index 3f815a2cb..3de9874da 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
@@ -5,6 +5,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
+import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.databinding.DialogExamBinding
import io.github.wulkanowy.utils.lifecycleAwareVariable
@@ -47,7 +48,9 @@ class ExamDialog : DialogFragment() {
examDialogTypeValue.text = exam.type
examDialogTeacherValue.text = exam.teacher
examDialogDateValue.text = exam.entryDate.toFormattedString()
- examDialogDescriptionValue.text = exam.description
+ examDialogDescriptionValue.text = exam.description.ifBlank {
+ getString(R.string.all_no_data)
+ }
examDialogClose.setOnClickListener { dismiss() }
}
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 a9d9039dd..3c747b949 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,11 +10,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.enums.GradeColorTheme
import io.github.wulkanowy.databinding.DialogGradeBinding
-import io.github.wulkanowy.utils.colorStringId
-import io.github.wulkanowy.utils.getBackgroundColor
-import io.github.wulkanowy.utils.getGradeColor
-import io.github.wulkanowy.utils.lifecycleAwareVariable
-import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.utils.*
class GradeDetailsDialog : DialogFragment() {
@@ -80,9 +76,7 @@ class GradeDetailsDialog : DialogFragment() {
setBackgroundResource(grade.getBackgroundColor(gradeColorTheme))
}
- gradeDialogTeacherValue.text = if (grade.teacher.isBlank()) {
- getString(R.string.all_no_data)
- } else grade.teacher
+ gradeDialogTeacherValue.text = grade.teacher.ifBlank { getString(R.string.all_no_data) }
gradeDialogDescriptionValue.text = grade.run {
when {
From 18568c86beec256ca63cbd85636cad592ff43d76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sat, 12 Feb 2022 12:19:25 +0100
Subject: [PATCH 059/669] New Crowdin updates (#1776)
---
app/src/main/res/values-ru/strings.xml | 36 +++++++++++++-------------
app/src/main/res/values-uk/strings.xml | 36 +++++++++++++-------------
2 files changed, 36 insertions(+), 36 deletions(-)
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index b5ad3683e..2ae1aad7f 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -204,17 +204,17 @@
Дополнительные уроки
Показать дополнительные уроки
Нет информации о дополнительных уроках
- New lesson
- New additional lesson
- Additional lesson added successfully
- Additional lesson deleted successfully
- Repeat weekly
- Delete additional lesson
- Just this lesson
- All in the series
- Start time
- End time
- End time must be greater than start time
+ Новый урок
+ Новый дополнительный урок
+ Дополнительный урок успешно добавлен
+ Дополнительный урок успешно удален
+ Повторять еженедельно
+ Удалить дополнительный урок
+ Просто этот урок
+ Все в серии
+ Время начала
+ Время окончания
+ Время окончания должно быть больше, чем время начала
Итоговая посещаемость
Отсутствие по школьным причинам
@@ -264,10 +264,10 @@
- Новые экзамены
- - %d new exam
- - %d new exams
- - %d new exams
- - %d new exams
+ - %d новый экзамен
+ - %d новый экзамен
+ - %d новый экзамен
+ - %d новых экзаменов
- %d экзамен
@@ -679,9 +679,9 @@
На вашем устройстве могут быть проблемы с синхронизацией данных и уведомлениями.\n\nЧтобы их исправить, вам необходимо добавить Wulkanowy в авто-старт и выключить оптимизацию/экономию батареи в настройках устройства.
Показывать дебаг-уведомления
Синхронизация отключена
- Official app notifications
+ Официальные уведомления приложения
Записывать официальные уведомления
- Remove official app notifications after capture
+ Удалить уведомления от официального приложения после захвата
Показывать push-уведомления
С помощью этой функции вы можете получить замену push-уведомлений, как в официальном приложении. Все, что вам нужно сделать, это разрешить Wulkanowy получать все уведомления в настройках системы.\n\nКак это работает?\nКогда вы получаете уведомление в Dziennik VULCAN, Wulkanowy будет уведомлен (это требует дополнительных прав) и запустит синхронизацию, чтобы отправить свое уведомление.\n\nТОЛЬКО ДЛЯ ПОЛЬЗОВАТЕЛЯ
Показывать уведомления о будущих уроках
@@ -760,7 +760,7 @@
Не удалось обновить! Wulkanowy может работать некорректно. Рассмотрите возможность обновления
Нет интернет-подключения
- An error occurred. Check your device clock
+ Произошла ошибка. Проверьте часы вашего устройства
Не удалось подключиться к регистрации. Серверы могут быть перегружены. Пожалуйста, повторите попытку позже
Не удалось загрузить данные. Пожалуйста, повторите попытку позже
Необходимо изменить пароль реестра
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 0c88df317..9dedb2060 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -204,17 +204,17 @@
Додаткові уроки
Показати додаткові уроки
Немає інформації про додаткових уроків
- New lesson
- New additional lesson
- Additional lesson added successfully
- Additional lesson deleted successfully
- Repeat weekly
- Delete additional lesson
- Just this lesson
- All in the series
- Start time
- End time
- End time must be greater than start time
+ Новий урок
+ Новий додатковий урок
+ Додатковий урок успішно додано
+ Успішно видалено додаткове заняття
+ Повторювати щотижня
+ Видалити додатковий урок
+ Тільки цей урок
+ Все в серії
+ Час початку
+ Час завершення
+ Час завершення має бути більшим, ніж час початку
Підсумок відвідуваності
Відсутність зі шкільних причин
@@ -264,10 +264,10 @@
- Нові іспити
- - %d new exam
- - %d new exams
- - %d new exams
- - %d new exams
+ - %d новий екзамен
+ - %d новий екзамен
+ - %d новий екзамен
+ - %d нових іспитів
- %d екзамен
@@ -679,9 +679,9 @@
На вашому пристрої можуть бути помилки з синхронізацією і повідомленнями\n\nЩоб виправити іх, вам необхідно додати Wulkanowy в авто-старт и вимкнути оптимізацію/экономію батареї в налаштуваннях пристрою.
Показувати дебаг-повідомлення
Синхронізація вимкнена
- Official app notifications
+ Офіційні сповіщення додатків
Захоплювати офіційні сповіщення програм
- Remove official app notifications after capture
+ Видалити офіційні сповіщення програм після захоплення
Показувати push-повідомлення
За допомогою цієї функції ви можете отримати заміну push -повідомлень, як у офіційному додатку. Все, що вам потрібно зробити, це дозволити Wulkanowy отримувати всі сповіщення у налаштуваннях вашої системи. \ N \ nЯк це працює? \ NКоли ви отримаєте сповіщення у Dziennik VULCAN, Wulkanowy отримає сповіщення (для цього призначені ці додаткові дозволи) і запустить синхронізація, яка може надсилати власне сповіщення. \ n \ n ТІЛЬКИ ДЛЯ РОЗШИРЕНИХ КОРИСТУВАЧІВ
Показувати повідомлення о наступних уроках
@@ -760,7 +760,7 @@
Помилка оновлення! Wulkanowy може не працювати належним чином. Подумайте про оновлення
Брак з\'єднання з інтернетом
- An error occurred. Check your device clock
+ Сталася помилка. Перевірте годинник пристрою
Помилка підключення до реєстрації. Сервери можуть бути перевантажені. Будь-ласка спробуйте пізніше
Помилка завантаження даних. Будь-ласка спробуйте пізніше
Потрібна реєстрація зміни пароля
From edd1c9442e080ea9367a66abef8a84b98a242a1d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sat, 12 Feb 2022 22:22:15 +0100
Subject: [PATCH 060/669] Fix calc vulcan average in second semester (#1779)
---
.../ui/modules/grade/GradeAverageProvider.kt | 26 +++++------
.../modules/grade/GradeAverageProviderTest.kt | 45 +++++++++++++++++--
2 files changed, 54 insertions(+), 17 deletions(-)
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 2784f429f..0c18ce95e 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
@@ -10,9 +10,7 @@ import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.ALL_YEAR
-import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.BOTH_SEMESTERS
-import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.ONE_SEMESTER
+import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.*
import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.changeModifier
import io.github.wulkanowy.utils.flowWithResourceIn
@@ -144,20 +142,20 @@ class GradeAverageProvider @Inject constructor(
isGradeAverageForceCalc: Boolean,
secondSemesterSubject: GradeSubject,
firstSemesterSubject: GradeSubject?
- ): Double {
+ ): Double = if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
- return if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
- val secondSemesterAverage =
- secondSemesterSubject.grades.updateModifiers(student)
- .calcAverage(isOptionalArithmeticAverage)
- val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
- ?.calcAverage(isOptionalArithmeticAverage) ?: secondSemesterAverage
+ val secondSemesterAverage = secondSemesterSubject.grades.updateModifiers(student)
+ .calcAverage(isOptionalArithmeticAverage)
+ val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
+ ?.calcAverage(isOptionalArithmeticAverage) ?: secondSemesterAverage
- (secondSemesterAverage + firstSemesterAverage) / divider
- } else {
- (secondSemesterSubject.average + (firstSemesterSubject?.average ?: secondSemesterSubject.average)) / divider
- }
+ (secondSemesterAverage + firstSemesterAverage) / divider
+ } else {
+ val divider = if (secondSemesterSubject.average > 0) 2 else 1
+
+ (secondSemesterSubject.average + (firstSemesterSubject?.average
+ ?: secondSemesterSubject.average)) / divider
}
private fun getGradeSubjects(
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 f097cb845..5e8c4c119 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
@@ -859,12 +859,51 @@ class GradeAverageProviderTest {
) to listOf(getSummary(semesterId = 23, subject = "Fizyka", average = .0))
}
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
- assertEquals(5.5555, items.single { it.subject == "Fizyka" }.average, .0001) // (from details): 5.72727272 + 4,8 → .average()
+ assertEquals(
+ 5.5555,
+ items.single { it.subject == "Fizyka" }.average,
+ .0001
+ ) // (from details): 5.72727272 + 4,8 → .average()
}
- private fun getGrade(semesterId: Int, subject: String, value: Double, modifier: Double = 0.0, weight: Double = 1.0, entry: String = ""): Grade {
+ @Test
+ fun `calc both semesters average when both summary have same average from vulcan and second semester has no grades`() {
+ every { preferencesRepository.gradeAverageForceCalc } returns false
+ every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
+ every { preferencesRepository.isOptionalArithmeticAverage } returns false
+
+ coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns
+ flowWithResource { firstGrades to firstSummaries }
+ coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns
+ flowWithResource { listOf() to firstSummaries }
+
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
+
+ assertEquals(3.1, items.single { it.subject == "Fizyka" }.average, .0001)
+ }
+
+ private fun getGrade(
+ semesterId: Int,
+ subject: String,
+ value: Double,
+ modifier: Double = 0.0,
+ weight: Double = 1.0,
+ entry: String = ""
+ ): Grade {
return Grade(
studentId = 101,
semesterId = semesterId,
From dec2703cc7a36a59eec6b90360e4245644502b66 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Feb 2022 20:05:45 +0000
Subject: [PATCH 061/669] Bump firebase-bom from 29.0.4 to 29.1.0 (#1782)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 92f76303c..5bfc0b8ea 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -231,7 +231,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
- playImplementation platform('com.google.firebase:firebase-bom:29.0.4')
+ playImplementation platform('com.google.firebase:firebase-bom:29.1.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From d3bf5c3e0ac32e8dfaf856476d1249d44fa6249a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Feb 2022 20:06:05 +0000
Subject: [PATCH 062/669] Bump lifecycle-livedata-ktx from 2.4.0 to 2.4.1
(#1781)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 5bfc0b8ea..e9029a23b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -204,7 +204,7 @@ dependencies {
implementation "androidx.work:work-runtime-ktx:$work_manager"
playImplementation "androidx.work:work-gcm:$work_manager"
- implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0"
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
implementation "androidx.room:room-runtime:$room"
implementation "androidx.room:room-ktx:$room"
From aff0fb3a6028bafeac7c91fb22bf9a3872f13840 Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Tue, 15 Feb 2022 13:08:44 +0100
Subject: [PATCH 063/669] Add information about student in grade statistics pie
chart (#1749)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Mikołaj Pich
---
app/build.gradle | 2 +
.../db/entities/GradeSemesterStatistics.kt | 5 +-
.../repositories/GradeStatisticsRepository.kt | 59 +++++++------
.../statistics/GradeStatisticsAdapter.kt | 45 +++++++---
app/src/main/res/values-cs/strings.xml | 2 +-
app/src/main/res/values-de/strings.xml | 2 +-
app/src/main/res/values-pl/strings.xml | 2 +-
app/src/main/res/values-ru/strings.xml | 2 +-
app/src/main/res/values-sk/strings.xml | 2 +-
app/src/main/res/values-uk/strings.xml | 2 +-
app/src/main/res/values/strings.xml | 4 +-
.../GradeStatisticsRepositoryTest.kt | 85 +++++++++++++++----
12 files changed, 153 insertions(+), 59 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index e9029a23b..609a54eb9 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -73,6 +73,8 @@ android {
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
}
debug {
+ minifyEnabled false
+ shrinkResources false
resValue "string", "app_name", "Wulkanowy DEV"
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt
index e747271ce..9e08b86bc 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt
@@ -24,5 +24,8 @@ data class GradeSemesterStatistics(
var id: Long = 0
@Transient
- var average: String = ""
+ var classAverage: String = ""
+
+ @Transient
+ var studentAverage: String = ""
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
index 356c203d0..4d26c3126 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
@@ -63,20 +63,16 @@ class GradeStatisticsRepository @Inject constructor(
mapResult = { items ->
when (subjectName) {
"Wszystkie" -> {
- val numerator = items.map {
- it.classAverage.replace(",", ".").toDoubleOrNull() ?: .0
- }.filterNot { it == .0 }
- (items.reversed() + GradePartialStatistics(
+ val summaryItem = GradePartialStatistics(
studentId = semester.studentId,
semesterId = semester.semesterId,
subject = subjectName,
- classAverage = if (numerator.isEmpty()) "" else numerator.average().let {
- "%.2f".format(Locale.FRANCE, it)
- },
- studentAverage = "",
+ classAverage = items.map { it.classAverage }.getSummaryAverage(),
+ studentAverage = items.map { it.studentAverage }.getSummaryAverage(),
classAmounts = items.map { it.classAmounts }.sumGradeAmounts(),
studentAmounts = items.map { it.studentAmounts }.sumGradeAmounts()
- )).reversed()
+ )
+ listOf(summaryItem) + items
}
else -> items.filter { it.subject == subjectName }
}.mapPartialToStatisticItems()
@@ -112,29 +108,29 @@ class GradeStatisticsRepository @Inject constructor(
val itemsWithAverage = items.map { item ->
item.copy().apply {
val denominator = item.amounts.sum()
- average = if (denominator == 0) "" else {
+ classAverage = if (denominator == 0) "" else {
(item.amounts.mapIndexed { gradeValue, amount ->
(gradeValue + 1) * amount
- }.sum().toDouble() / denominator).let {
- "%.2f".format(Locale.FRANCE, it)
- }
+ }.sum().toDouble() / denominator).asAverageString()
}
}
}
when (subjectName) {
- "Wszystkie" -> (itemsWithAverage.reversed() + GradeSemesterStatistics(
- studentId = semester.studentId,
- semesterId = semester.semesterId,
- subject = subjectName,
- amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(),
- studentGrade = 0
- ).apply {
- average = itemsWithAverage.mapNotNull {
- it.average.replace(",", ".").toDoubleOrNull()
- }.average().let {
- "%.2f".format(Locale.FRANCE, it)
+ "Wszystkie" -> {
+ val summaryItem = GradeSemesterStatistics(
+ studentId = semester.studentId,
+ semesterId = semester.semesterId,
+ subject = subjectName,
+ amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(),
+ studentGrade = 0,
+ ).apply {
+ classAverage = itemsWithAverage.map { it.classAverage }.getSummaryAverage()
+ studentAverage = items
+ .mapNotNull { summary -> summary.studentGrade.takeIf { it != 0 } }
+ .average().asAverageString()
}
- }).reversed()
+ listOf(summaryItem) + itemsWithAverage
+ }
else -> itemsWithAverage.filter { it.subject == subjectName }
}.mapSemesterToStatisticItems()
}
@@ -171,6 +167,19 @@ class GradeStatisticsRepository @Inject constructor(
}
)
+ private fun List.getSummaryAverage(): String {
+ val averages = mapNotNull {
+ it.replace(",", ".").toDoubleOrNull()
+ }
+
+ return averages.average()
+ .asAverageString()
+ .takeIf { averages.isNotEmpty() }
+ .orEmpty()
+ }
+
+ private fun Double.asAverageString(): String = "%.2f".format(Locale.FRANCE, this)
+
private fun List>.sumGradeAmounts(): List {
val result = mutableListOf(0, 0, 0, 0, 0, 0)
forEach {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt
index 6be2d969f..fd0ac5471 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsAdapter.kt
@@ -9,12 +9,7 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.github.mikephil.charting.components.Legend
import com.github.mikephil.charting.components.LegendEntry
-import com.github.mikephil.charting.data.BarData
-import com.github.mikephil.charting.data.BarDataSet
-import com.github.mikephil.charting.data.BarEntry
-import com.github.mikephil.charting.data.PieData
-import com.github.mikephil.charting.data.PieDataSet
-import com.github.mikephil.charting.data.PieEntry
+import com.github.mikephil.charting.data.*
import com.github.mikephil.charting.formatter.ValueFormatter
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
@@ -136,20 +131,50 @@ class GradeStatisticsAdapter @Inject constructor() :
binding: ItemGradeStatisticsPieBinding,
partials: GradePartialStatistics
) {
- bindPieChart(binding, partials.subject, partials.classAverage, partials.classAmounts)
+ val studentAverage = partials.studentAverage.takeIf { it.isNotEmpty() }?.let {
+ binding.root.context.getString(R.string.grade_statistics_student_average, it)
+ }
+ bindPieChart(
+ binding = binding,
+ subject = partials.subject,
+ average = partials.classAverage,
+ studentValue = studentAverage,
+ amounts = partials.classAmounts
+ )
}
private fun bindSemesterChart(
binding: ItemGradeStatisticsPieBinding,
semester: GradeSemesterStatistics
) {
- bindPieChart(binding, semester.subject, semester.average, semester.amounts)
+ val studentAverage = semester.studentAverage.takeIf { it.isNotBlank() }
+ val studentGrade = semester.studentGrade.takeIf { it != 0 }
+
+ val studentValue = when {
+ studentAverage != null -> binding.root.context.getString(
+ R.string.grade_statistics_student_average,
+ studentAverage
+ )
+ studentGrade != null -> binding.root.context.getString(
+ R.string.grade_statistics_student_grade,
+ studentGrade.toString()
+ )
+ else -> null
+ }
+ bindPieChart(
+ binding = binding,
+ subject = semester.subject,
+ average = semester.classAverage,
+ studentValue = studentValue,
+ amounts = semester.amounts
+ )
}
private fun bindPieChart(
binding: ItemGradeStatisticsPieBinding,
subject: String,
average: String,
+ studentValue: String?,
amounts: List
) {
with(binding.gradeStatisticsPieTitle) {
@@ -208,13 +233,13 @@ class GradeStatisticsAdapter @Inject constructor() :
val numberOfGradesString = amounts.fold(0) { acc, it -> acc + it }
.let { resources.getQuantityString(R.plurals.grade_number_item, it, it) }
val averageString =
- binding.root.context.getString(R.string.grade_statistics_average, average)
+ binding.root.context.getString(R.string.grade_statistics_class_average, average)
minAngleForSlices = 25f
description.isEnabled = false
centerText =
numberOfGradesString + ("\n\n" + averageString).takeIf { average.isNotBlank() }
- .orEmpty()
+ .orEmpty() + studentValue?.let { "\n$it" }.orEmpty()
setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index b56617ed7..e7c2da332 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -105,7 +105,7 @@
Semestr
Body
Vysvětlivky
- Průměr: %1$s
+ Průměr: %1$s
Třída
Žák
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 5b2fee80d..68c371d51 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -105,7 +105,7 @@
Semester
Punkte
Legende
- Durchschnitt: %1$s
+ Durchschnitt: %1$s
Klasse
Schüler
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 208e6f3ea..da127fe2d 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -105,7 +105,7 @@
Semestralne
Punkty
Legenda
- Średnia: %1$s
+ Średnia: %1$s
Klasa
Uczeń
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 2ae1aad7f..e6e9834f5 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -105,7 +105,7 @@
За семестр
Баллы
Легенда
- Средняя: %1$s
+ Средняя: %1$s
Класс
Студент
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 30365fe56..ba701eafd 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -105,7 +105,7 @@
Semester
Body
Vysvetlivky
- Priemer: %1$s
+ Priemer: %1$s
Trieda
Žiák
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 9dedb2060..7bcffe01b 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -105,7 +105,7 @@
Семестрові
Бали
Умовні позначення
- Середня оцінка: %1$s
+ Середня оцінка: %1$s
Клас
Учень
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9c63073e2..d565d65cc 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -115,7 +115,9 @@
Semester
Points
Legend
- Average: %1$s
+ Class average: %1$s
+ Your average: %1$s
+ Your grade: %1$s
Class
Student
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt
index cce3794de..6221b6989 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt
@@ -11,14 +11,9 @@ import io.github.wulkanowy.sdk.pojo.GradeStatisticsItem
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.toFirstResult
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerify
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
-import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
@@ -48,22 +43,27 @@ class GradeStatisticsRepositoryTest {
private lateinit var gradeStatisticsRepository: GradeStatisticsRepository
- private val remotePartialList = listOf(
- getGradeStatisticsPartialSubject("Fizyka"),
- getGradeStatisticsPartialSubject("Matematyka")
- )
-
@Before
fun setUp() {
MockKAnnotations.init(this)
every { refreshHelper.shouldBeRefreshed(any()) } returns false
- gradeStatisticsRepository = GradeStatisticsRepository(gradePartialStatisticsDb, gradePointsStatisticsDb, gradeSemesterStatisticsDb, sdk, refreshHelper)
+ gradeStatisticsRepository = GradeStatisticsRepository(
+ gradePartialStatisticsDb = gradePartialStatisticsDb,
+ gradePointsStatisticsDb = gradePointsStatisticsDb,
+ gradeSemesterStatisticsDb = gradeSemesterStatisticsDb,
+ sdk = sdk,
+ refreshHelper = refreshHelper,
+ )
}
@Test
fun `force refresh without difference`() {
// prepare
+ val remotePartialList = listOf(
+ getGradeStatisticsPartialSubject("Fizyka"),
+ getGradeStatisticsPartialSubject("Matematyka")
+ )
coEvery { sdk.getGradesPartialStatistics(1) } returns remotePartialList
coEvery { gradePartialStatisticsDb.loadAll(1, 1) } returnsMany listOf(
flowOf(remotePartialList.mapToEntities(semester)),
@@ -73,21 +73,74 @@ class GradeStatisticsRepositoryTest {
coEvery { gradePartialStatisticsDb.deleteAll(any()) } just Runs
// execute
- val res = runBlocking { gradeStatisticsRepository.getGradesPartialStatistics(student, semester, "Wszystkie", true).toFirstResult() }
+ val res = runBlocking {
+ gradeStatisticsRepository.getGradesPartialStatistics(
+ student = student,
+ semester = semester,
+ subjectName = "Wszystkie",
+ forceRefresh = true,
+ ).toFirstResult()
+ }
+ val items = res.data.orEmpty()
// verify
assertEquals(null, res.error)
assertEquals(2 + 1, res.data?.size)
+ assertEquals("", items[0].partial?.studentAverage)
+ assertEquals("", items[1].partial?.studentAverage)
+ assertEquals("", items[2].partial?.studentAverage)
coVerify { sdk.getGradesPartialStatistics(1) }
coVerify { gradePartialStatisticsDb.loadAll(1, 1) }
coVerify { gradePartialStatisticsDb.insertAll(match { it.isEmpty() }) }
coVerify { gradePartialStatisticsDb.deleteAll(match { it.isEmpty() }) }
}
- private fun getGradeStatisticsPartialSubject(subjectName: String) = GradeStatisticsSubject(
+ @Test
+ fun `force refresh without difference with filled up items`() {
+ // prepare
+ val remotePartialList = listOf(
+ getGradeStatisticsPartialSubject("Fizyka", "1.0"),
+ getGradeStatisticsPartialSubject("Matematyka", "5.0")
+ )
+ coEvery { sdk.getGradesPartialStatistics(1) } returns remotePartialList
+ coEvery { gradePartialStatisticsDb.loadAll(1, 1) } returnsMany listOf(
+ flowOf(remotePartialList.mapToEntities(semester)),
+ flowOf(remotePartialList.mapToEntities(semester))
+ )
+ coEvery { gradePartialStatisticsDb.insertAll(any()) } returns listOf(1, 2, 3)
+ coEvery { gradePartialStatisticsDb.deleteAll(any()) } just Runs
+
+ // execute
+ val res = runBlocking {
+ gradeStatisticsRepository.getGradesPartialStatistics(
+ student = student,
+ semester = semester,
+ subjectName = "Wszystkie",
+ forceRefresh = true,
+ ).toFirstResult()
+ }
+ val items = res.data.orEmpty()
+
+ // verify
+ assertEquals(null, res.error)
+ assertEquals(2 + 1, res.data?.size)
+ assertEquals("3,00", items[0].partial?.studentAverage)
+ assertEquals("1.0", items[1].partial?.studentAverage)
+ assertEquals("5.0", items[2].partial?.studentAverage)
+ coVerify { sdk.getGradesPartialStatistics(1) }
+ coVerify { gradePartialStatisticsDb.loadAll(1, 1) }
+ coVerify { gradePartialStatisticsDb.insertAll(match { it.isEmpty() }) }
+ coVerify { gradePartialStatisticsDb.deleteAll(match { it.isEmpty() }) }
+ }
+
+ private fun getGradeStatisticsPartialSubject(
+ subjectName: String,
+ studentAverage: String = "",
+ classAverage: String = "",
+ ) = GradeStatisticsSubject(
subject = subjectName,
- studentAverage = "",
- classAverage = "",
+ studentAverage = studentAverage,
+ classAverage = classAverage,
classItems = listOf(
GradeStatisticsItem(
subject = subjectName,
From 820b99dbc7ab78132a5abd99833c9d1d1f36eea5 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Feb 2022 19:35:02 +0000
Subject: [PATCH 064/669] Bump hilt_version from 2.40.5 to 2.41 (#1786)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index ab598e633..0571396e3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ buildscript {
ext {
kotlin_version = '1.6.10'
about_libraries = '8.9.4'
- hilt_version = "2.40.5"
+ hilt_version = "2.41"
}
repositories {
mavenCentral()
From e7561d4794af3c51f1e3fb9ecdffdb5b4885cbcd Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Feb 2022 18:32:08 +0000
Subject: [PATCH 065/669] Bump room from 2.4.1 to 2.4.2 (#1794)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 609a54eb9..ca5f802f4 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -171,7 +171,7 @@ huaweiPublish {
ext {
work_manager = "2.7.1"
android_hilt = "1.0.0"
- room = "2.4.1"
+ room = "2.4.2"
chucker = "3.5.2"
mockk = "1.12.2"
coroutines = "1.6.0"
From 8915c5dd8ecc9622bd3efe2ae8b027997e1819a1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Feb 2022 18:32:31 +0000
Subject: [PATCH 066/669] Bump agconnect-crash from 1.6.4.200 to 1.6.4.300
(#1793)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index ca5f802f4..3b73a92f7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -242,7 +242,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:20.5.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.4.0.300'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.4.200'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.4.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From 9a413c14c329ebb0e0f741c566e2f2555a864bb0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Feb 2022 18:37:29 +0000
Subject: [PATCH 067/669] Bump agcp from 1.6.4.200 to 1.6.4.300 (#1791)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 0571396e3..1b7e01bf7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.1.1'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
- classpath 'com.huawei.agconnect:agcp:1.6.4.200'
+ classpath 'com.huawei.agconnect:agcp:1.6.4.300'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.1"
From f48caf9f70afdca48e22d3104b4dc3235d1c66c1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Feb 2022 18:41:16 +0000
Subject: [PATCH 068/669] Bump play-services-ads from 20.5.0 to 20.6.0 (#1792)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 3b73a92f7..960096acc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -239,7 +239,7 @@ dependencies {
playImplementation 'com.google.firebase:firebase-crashlytics:'
playImplementation 'com.google.android.play:core:1.10.3'
playImplementation 'com.google.android.play:core-ktx:1.8.1'
- playImplementation 'com.google.android.gms:play-services-ads:20.5.0'
+ playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.4.0.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.4.300'
From c3abe50ed4d074426af3027d39c5e2195fa881db Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Feb 2022 18:59:53 +0000
Subject: [PATCH 069/669] Bump gradle from 7.1.1 to 7.1.2 (#1790)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 1b7e01bf7..1a97f58ce 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
- classpath 'com.android.tools.build:gradle:7.1.1'
+ classpath 'com.android.tools.build:gradle:7.1.2'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.huawei.agconnect:agcp:1.6.4.300'
From 57ea6379aba1b5271aff1e72c0d5482d3848f682 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sun, 13 Mar 2022 04:01:14 +0100
Subject: [PATCH 070/669] Timetable timer refactor (#1785)
---
.editorconfig | 12 +
.../ui/modules/timetable/TimetableAdapter.kt | 226 ++++++------------
.../ui/modules/timetable/TimetableFragment.kt | 24 +-
.../ui/modules/timetable/TimetableItem.kt | 30 +++
.../modules/timetable/TimetablePresenter.kt | 96 +++++---
.../ui/modules/timetable/TimetableView.kt | 8 +-
6 files changed, 190 insertions(+), 206 deletions(-)
create mode 100644 .editorconfig
create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000..35fbd4661
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+[*]
+charset=utf-8
+end_of_line=lf
+insert_final_newline=true
+indent_style=space
+indent_size=4
+
+[*.json]
+indent_size=2
+
+[*.{kt,kts}]
+disabled_rules=import-ordering,no-wildcard-imports
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
index eacd12c69..d6917672a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
@@ -1,116 +1,69 @@
package io.github.wulkanowy.ui.modules.timetable
-import android.graphics.Paint
-import android.os.Handler
-import android.os.Looper
import android.view.LayoutInflater
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.widget.TextView
+import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Timetable
-import io.github.wulkanowy.data.enums.TimetableMode
import io.github.wulkanowy.databinding.ItemTimetableBinding
import io.github.wulkanowy.databinding.ItemTimetableSmallBinding
-import io.github.wulkanowy.utils.*
-import timber.log.Timber
-import java.time.Instant
-import java.util.*
+import io.github.wulkanowy.utils.getThemeAttrColor
+import io.github.wulkanowy.utils.toFormattedString
import javax.inject.Inject
-import kotlin.concurrent.timer
-class TimetableAdapter @Inject constructor() : RecyclerView.Adapter() {
+class TimetableAdapter @Inject constructor() :
+ ListAdapter(differ) {
- private enum class ViewType {
- ITEM_NORMAL,
- ITEM_SMALL
- }
-
- var onClickListener: (Timetable) -> Unit = {}
-
- private var showWholeClassPlan = TimetableMode.ONLY_CURRENT_GROUP
-
- private var showGroupsInPlan: Boolean = false
-
- private var showTimers: Boolean = false
-
- private val timers = mutableMapOf()
-
- private val items = mutableListOf()
-
- fun submitList(
- newTimetable: List,
- showWholeClassPlan: TimetableMode = this.showWholeClassPlan,
- showGroupsInPlan: Boolean = this.showGroupsInPlan,
- showTimers: Boolean = this.showTimers
- ) {
- val isFlagsDifferent = this.showWholeClassPlan != showWholeClassPlan
- || this.showGroupsInPlan != showGroupsInPlan
- || this.showTimers != showTimers
-
- val diffResult = DiffUtil.calculateDiff(
- TimetableAdapterDiffCallback(
- oldList = items.toMutableList(),
- newList = newTimetable,
- isFlagsDifferent = isFlagsDifferent
- )
- )
-
- this.showGroupsInPlan = showGroupsInPlan
- this.showTimers = showTimers
- this.showWholeClassPlan = showWholeClassPlan
-
- items.clear()
- items.addAll(newTimetable)
-
- diffResult.dispatchUpdatesTo(this)
- }
-
- fun clearTimers() {
- Timber.d("Timetable timers (${timers.size}) cleared")
- with(timers) {
- forEach { (_, timer) ->
- timer?.cancel()
- timer?.purge()
- }
- clear()
- }
- }
-
- override fun getItemCount() = items.size
-
- override fun getItemViewType(position: Int) = when {
- !items[position].isStudentPlan && showWholeClassPlan == TimetableMode.SMALL_OTHER_GROUP -> ViewType.ITEM_SMALL.ordinal
- else -> ViewType.ITEM_NORMAL.ordinal
- }
+ override fun getItemViewType(position: Int): Int = getItem(position).type.ordinal
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
- return when (viewType) {
- ViewType.ITEM_NORMAL.ordinal -> ItemViewHolder(
- ItemTimetableBinding.inflate(inflater, parent, false)
- )
- ViewType.ITEM_SMALL.ordinal -> SmallItemViewHolder(
+ return when (TimetableItemType.values()[viewType]) {
+ TimetableItemType.SMALL -> SmallViewHolder(
ItemTimetableSmallBinding.inflate(inflater, parent, false)
)
- else -> throw IllegalStateException()
+ TimetableItemType.NORMAL -> NormalViewHolder(
+ ItemTimetableBinding.inflate(inflater, parent, false)
+ )
}
}
+ override fun onBindViewHolder(
+ holder: RecyclerView.ViewHolder,
+ position: Int,
+ payloads: MutableList
+ ) {
+ if (payloads.isEmpty()) return super.onBindViewHolder(holder, position, payloads)
+
+ if (holder is NormalViewHolder) updateTimeLeft(
+ binding = holder.binding,
+ timeLeft = (getItem(position) as TimetableItem.Normal).timeLeft,
+ )
+ }
+
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
- val lesson = items[position]
-
when (holder) {
- is ItemViewHolder -> bindNormalView(holder.binding, lesson, position)
- is SmallItemViewHolder -> bindSmallView(holder.binding, lesson)
+ is SmallViewHolder -> bindSmallView(
+ binding = holder.binding,
+ item = getItem(position) as TimetableItem.Small,
+ )
+ is NormalViewHolder -> bindNormalView(
+ binding = holder.binding,
+ item = getItem(position) as TimetableItem.Normal,
+ )
}
}
- private fun bindSmallView(binding: ItemTimetableSmallBinding, lesson: Timetable) {
+ private fun bindSmallView(binding: ItemTimetableSmallBinding, item: TimetableItem.Small) {
+ val lesson = item.lesson
+
with(binding) {
timetableSmallItemNumber.text = lesson.number.toString()
timetableSmallItemSubject.text = lesson.subject
@@ -122,11 +75,13 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter i < position && !item.isStudentPlan }.size)
- ?.let {
- if (!it.canceled && it.isStudentPlan) it.end
- else null
- }
- }
-
- private fun updateTimeLeft(binding: ItemTimetableBinding, lesson: Timetable, position: Int) {
- val isShowTimeUntil = lesson.isShowTimeUntil(getPreviousLesson(position))
- val until = lesson.until.plusMinutes(1)
- val left = lesson.left?.plusMinutes(1)
- val isJustFinished = lesson.isJustFinished
-
+ private fun updateTimeLeft(binding: ItemTimetableBinding, timeLeft: TimeLeft?) {
with(binding) {
when {
// before lesson
- isShowTimeUntil -> {
- Timber.d("Show time until lesson: $position")
+ timeLeft?.until != null -> {
timetableItemTimeLeft.visibility = GONE
with(timetableItemTimeUntil) {
visibility = VISIBLE
@@ -189,14 +112,13 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter {
- Timber.d("Show time left lesson: $position")
+ timeLeft?.left != null -> {
timetableItemTimeUntil.visibility = GONE
with(timetableItemTimeLeft) {
visibility = VISIBLE
@@ -204,14 +126,13 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter {
- Timber.d("Show just finished lesson: $position")
+ timeLeft?.isJustFinished == true -> {
timetableItemTimeUntil.visibility = GONE
timetableItemTimeLeft.visibility = VISIBLE
timetableItemTimeLeft.text = root.context.getString(R.string.timetable_finished)
@@ -225,9 +146,7 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter,
- private val newList: List,
- private val isFlagsDifferent: Boolean
- ) : DiffUtil.Callback() {
+ companion object {
+ private val differ = object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: TimetableItem, newItem: TimetableItem): Boolean =
+ when {
+ oldItem is TimetableItem.Small && newItem is TimetableItem.Small -> {
+ oldItem.lesson.start == newItem.lesson.start
+ }
+ oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal -> {
+ oldItem.lesson.start == newItem.lesson.start
+ }
+ else -> oldItem == newItem
+ }
- override fun getOldListSize() = oldList.size
+ override fun areContentsTheSame(oldItem: TimetableItem, newItem: TimetableItem) =
+ oldItem == newItem
- override fun getNewListSize() = newList.size
-
- override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
- oldList[oldItemPosition].id == newList[newItemPosition].id
-
- override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
- oldList[oldItemPosition] == newList[newItemPosition] && !isFlagsDifferent
+ override fun getChangePayload(oldItem: TimetableItem, newItem: TimetableItem): Any? {
+ return if (oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal) {
+ if (oldItem.lesson == newItem.lesson && oldItem.timeLeft != newItem.timeLeft) {
+ "time_left"
+ } else super.getChangePayload(oldItem, newItem)
+ } else super.getChangePayload(oldItem, newItem)
+ }
+ }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
index f59c6432c..fdd4aface 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
@@ -12,7 +12,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Timetable
-import io.github.wulkanowy.data.enums.TimetableMode
import io.github.wulkanowy.databinding.FragmentTimetableBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
@@ -20,11 +19,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.modules.timetable.additional.AdditionalLessonsFragment
import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
-import io.github.wulkanowy.utils.dpToPx
-import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
-import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
-import io.github.wulkanowy.utils.openMaterialDatePicker
+import io.github.wulkanowy.utils.*
import java.time.LocalDate
import javax.inject.Inject
@@ -73,8 +68,6 @@ class TimetableFragment : BaseFragment(R.layout.fragme
}
override fun initView() {
- timetableAdapter.onClickListener = presenter::onTimetableItemSelected
-
with(binding.timetableRecycler) {
layoutManager = LinearLayoutManager(context)
adapter = timetableAdapter
@@ -110,18 +103,8 @@ class TimetableFragment : BaseFragment(R.layout.fragme
}
}
- override fun updateData(
- data: List,
- showWholeClassPlanType: TimetableMode,
- showGroupsInPlanType: Boolean,
- showTimetableTimers: Boolean
- ) {
- timetableAdapter.submitList(
- newTimetable = data.toMutableList(),
- showGroupsInPlan = showGroupsInPlanType,
- showTimers = showTimetableTimers,
- showWholeClassPlan = showWholeClassPlanType
- )
+ override fun updateData(data: List) {
+ timetableAdapter.submitList(data)
}
override fun clearData() {
@@ -214,7 +197,6 @@ class TimetableFragment : BaseFragment(R.layout.fragme
}
override fun onDestroyView() {
- timetableAdapter.clearTimers()
presenter.onDetachView()
super.onDestroyView()
}
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
new file mode 100644
index 000000000..92716ace8
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableItem.kt
@@ -0,0 +1,30 @@
+package io.github.wulkanowy.ui.modules.timetable
+
+import io.github.wulkanowy.data.db.entities.Timetable
+import java.time.Duration
+
+sealed class TimetableItem(val type: TimetableItemType) {
+
+ data class Small(
+ val lesson: Timetable,
+ val onClick: (Timetable) -> Unit,
+ ) : TimetableItem(TimetableItemType.SMALL)
+
+ data class Normal(
+ val lesson: Timetable,
+ val showGroupsInPlan: Boolean,
+ val timeLeft: TimeLeft?,
+ val onClick: (Timetable) -> Unit,
+ ) : TimetableItem(TimetableItemType.NORMAL)
+}
+
+data class TimeLeft(
+ val until: Duration?,
+ val left: Duration?,
+ val isJustFinished: Boolean,
+)
+
+enum class TimetableItemType {
+ SMALL,
+ NORMAL,
+}
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 87f5cac3d..ec3ef7b02 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
@@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.timetable
-import android.annotation.SuppressLint
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.enums.TimetableMode
@@ -10,25 +9,17 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.capitalise
-import io.github.wulkanowy.utils.flowWithResourceIn
-import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
-import io.github.wulkanowy.utils.isHolidays
-import io.github.wulkanowy.utils.nextOrSameSchoolDay
-import io.github.wulkanowy.utils.nextSchoolDay
-import io.github.wulkanowy.utils.previousSchoolDay
-import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
+import java.time.Instant
import java.time.LocalDate
-import java.time.LocalDate.now
-import java.time.LocalDate.of
-import java.time.LocalDate.ofEpochDay
+import java.time.LocalDate.*
+import java.util.*
import javax.inject.Inject
+import kotlin.concurrent.timer
class TimetablePresenter @Inject constructor(
errorHandler: ErrorHandler,
@@ -46,6 +37,8 @@ class TimetablePresenter @Inject constructor(
private lateinit var lastError: Throwable
+ private var tickTimer: Timer? = null
+
fun onAttachView(view: TimetableView, date: Long?) {
super.onAttachView(view)
view.initView()
@@ -106,11 +99,6 @@ class TimetablePresenter @Inject constructor(
}
}
- fun onTimetableItemSelected(lesson: Timetable) {
- Timber.i("Select timetable item ${lesson.id}")
- view?.showTimetableDialog(lesson)
- }
-
fun onAdditionalLessonsSwitchSelected(): Boolean {
view?.openAdditionalLessonsView()
return true
@@ -148,12 +136,12 @@ class TimetablePresenter @Inject constructor(
Status.LOADING -> {
if (!it.data?.lessons.isNullOrEmpty()) {
view?.run {
+ updateData(it.data!!.lessons)
enableSwipe(true)
showRefresh(true)
showErrorView(false)
showProgress(false)
showContent(true)
- updateData(it.data!!.lessons)
}
}
}
@@ -189,17 +177,62 @@ class TimetablePresenter @Inject constructor(
}
private fun updateData(lessons: List) {
- view?.updateData(
- showWholeClassPlanType = prefRepository.showWholeClassPlan,
- showGroupsInPlanType = prefRepository.showGroupsInPlan,
- showTimetableTimers = prefRepository.showTimetableTimers,
- data = createItems(lessons)
+ tickTimer?.cancel()
+
+ if (!prefRepository.showTimetableTimers) {
+ view?.updateData(createItems(lessons))
+ } else {
+ tickTimer = timer(period = 2_000) {
+ view?.updateData(createItems(lessons))
+ }
+ }
+ }
+
+ private fun createItems(items: List): List {
+ val filteredItems = items
+ .filter {
+ if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
+ it.isStudentPlan
+ } else true
+ }.sortedWith(
+ compareBy({ item -> item.number }, { item -> !item.isStudentPlan })
+ )
+
+ return filteredItems.mapIndexed { i, it ->
+ if (it.isStudentPlan) TimetableItem.Normal(
+ lesson = it,
+ showGroupsInPlan = prefRepository.showGroupsInPlan,
+ timeLeft = filteredItems.getTimeLeftForLesson(it, i),
+ onClick = ::onTimetableItemSelected
+ ) else TimetableItem.Small(
+ lesson = it,
+ onClick = ::onTimetableItemSelected
+ )
+ }
+ }
+
+ private fun List.getTimeLeftForLesson(lesson: Timetable, index: Int): TimeLeft {
+ val isShowTimeUntil = lesson.isShowTimeUntil(getPreviousLesson(index))
+ return TimeLeft(
+ until = lesson.until.plusMinutes(1).takeIf { isShowTimeUntil },
+ left = lesson.left?.plusMinutes(1),
+ isJustFinished = lesson.isJustFinished,
)
}
- private fun createItems(items: List) = items.filter { item ->
- if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) item.isStudentPlan else true
- }.sortedWith(compareBy({ item -> item.number }, { item -> !item.isStudentPlan }))
+ private fun List.getPreviousLesson(position: Int): Instant? {
+ return filter { it.isStudentPlan }
+ .getOrNull(position - 1 - filterIndexed { i, item -> i < position && !item.isStudentPlan }.size)
+ ?.let {
+ if (!it.canceled && it.isStudentPlan) it.end
+ else null
+ }
+ }
+
+ private fun onTimetableItemSelected(lesson: Timetable) {
+ Timber.i("Select timetable item ${lesson.id}")
+ view?.showTimetableDialog(lesson)
+ }
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
@@ -227,7 +260,6 @@ class TimetablePresenter @Inject constructor(
}
}
- @SuppressLint("DefaultLocale")
private fun reloadNavigation() {
view?.apply {
showPreButton(!currentDate.minusDays(1).isHolidays)
@@ -235,4 +267,10 @@ class TimetablePresenter @Inject constructor(
updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise())
}
}
+
+ override fun onDetachView() {
+ tickTimer?.cancel()
+ tickTimer = null
+ super.onDetachView()
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt
index 4f6af4b9e..8cfb26204 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt
@@ -1,7 +1,6 @@
package io.github.wulkanowy.ui.modules.timetable
import io.github.wulkanowy.data.db.entities.Timetable
-import io.github.wulkanowy.data.enums.TimetableMode
import io.github.wulkanowy.ui.base.BaseView
import java.time.LocalDate
@@ -13,12 +12,7 @@ interface TimetableView : BaseView {
fun initView()
- fun updateData(
- data: List,
- showWholeClassPlanType: TimetableMode,
- showGroupsInPlanType: Boolean,
- showTimetableTimers: Boolean
- )
+ fun updateData(data: List)
fun updateNavigationDay(date: String)
From a04ba4ae1022ca28a3e4eda438bb4d911f76db68 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sun, 13 Mar 2022 22:43:57 +0100
Subject: [PATCH 071/669] Login improvements (#1800)
* Update sdk
* Change default register variant name
* Change symbol hint message and email template
---
app/build.gradle | 2 +-
app/src/main/res/values/api_hosts.xml | 2 +-
app/src/main/res/values/strings.xml | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 960096acc..8467dd018 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -178,7 +178,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:1.5.0"
+ implementation "io.github.wulkanowy:sdk:fba873461e"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
diff --git a/app/src/main/res/values/api_hosts.xml b/app/src/main/res/values/api_hosts.xml
index 158490471..b3b434e14 100644
--- a/app/src/main/res/values/api_hosts.xml
+++ b/app/src/main/res/values/api_hosts.xml
@@ -1,7 +1,7 @@
- - Vulcan
+ - Standardowa
- Opolska eSzkoła
- Gdańska Platforma Edukacyjna
- Lubelski Portal Oświatowy
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d565d65cc..5712a6cc0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -59,7 +59,7 @@
Invalid symbol
Student not found. Validate the symbol and the chosen variation of the UONET+ register
Selected student is already logged in
- The symbol can be found on the register page in Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nMake sure that you have set the appropriate register variant in the UONET+ register variant field on the previous screen. Wulkanowy does not detect pre-school students at the moment
+ The symbol can be found on the register page in Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nMake sure that you have set the appropriate register variant in the UONET+ register variant field on the previous screen
Select students to log in to the application
Other options
In this mode, a lucky number does not work, a class grade stats, summary of attendance, excuse for absence, completed lessons, school information and preview of the list of registered devices
@@ -71,7 +71,7 @@
Discord
Send email
Zgłoszenie: Problemy z logowaniem
- Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nDodatkowe informacje: %4$s\nOstatni błąd: %5$s\n\nNazwa szkoły wraz z miejscowością i numer klasy:
+ Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nDodatkowe informacje: %4$s\nOstatni błąd: %5$s\n\nNazwa szkoły i miejscowość:
Make sure you select the correct UONET+ register variation!
I forgot my password
Recover your account
From 15537586c465ce1c0956735866e587e27208b293 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sun, 13 Mar 2022 22:47:54 +0100
Subject: [PATCH 072/669] Add exam date field to exam details dialog (#1801)
---
.../wulkanowy/ui/modules/exam/ExamDialog.kt | 3 +-
app/src/main/res/layout/dialog_exam.xml | 45 ++++++++++++++++---
2 files changed, 41 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
index 3de9874da..7b0ac90cf 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
@@ -47,7 +47,8 @@ class ExamDialog : DialogFragment() {
examDialogSubjectValue.text = exam.subject
examDialogTypeValue.text = exam.type
examDialogTeacherValue.text = exam.teacher
- examDialogDateValue.text = exam.entryDate.toFormattedString()
+ examDialogEntryDateValue.text = exam.entryDate.toFormattedString()
+ examDialogDeadlineDateValue.text = exam.date.toFormattedString()
examDialogDescriptionValue.text = exam.description.ifBlank {
getString(R.string.all_no_data)
}
diff --git a/app/src/main/res/layout/dialog_exam.xml b/app/src/main/res/layout/dialog_exam.xml
index 51153ac8e..f77ca553a 100644
--- a/app/src/main/res/layout/dialog_exam.xml
+++ b/app/src/main/res/layout/dialog_exam.xml
@@ -1,8 +1,10 @@
+ android:layout_height="match_parent"
+ tools:context=".ui.modules.exam.ExamDialog">
+ app:layout_constraintTop_toBottomOf="@id/examDialogDeadlineDateTitle" />
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/examDialogEntryDateValue" />
Date: Mon, 14 Mar 2022 00:38:40 +0100
Subject: [PATCH 073/669] Add the option to quickly add a calendar event from
the exam details (#1802)
* Extract intent utils to separate file
* Add add to calendar button in exam details dialog
* Set 8:00-8:45 start/end time
---
.../wulkanowy/ui/modules/exam/ExamDialog.kt | 10 ++
.../wulkanowy/utils/ContextExtension.kt | 81 +-------------
.../io/github/wulkanowy/utils/IntentUtils.kt | 100 ++++++++++++++++++
...c_calendat_all.xml => ic_calendar_all.xml} | 0
.../main/res/layout/dialog_additional_add.xml | 2 +-
app/src/main/res/layout/dialog_exam.xml | 40 ++++---
.../main/res/layout/dialog_homework_add.xml | 4 +-
app/src/main/res/values/strings.xml | 1 +
8 files changed, 144 insertions(+), 94 deletions(-)
create mode 100644 app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt
rename app/src/main/res/drawable/{ic_calendat_all.xml => ic_calendar_all.xml} (100%)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
index 7b0ac90cf..41adc008a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamDialog.kt
@@ -9,7 +9,9 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.databinding.DialogExamBinding
import io.github.wulkanowy.utils.lifecycleAwareVariable
+import io.github.wulkanowy.utils.openCalendarEventAdd
import io.github.wulkanowy.utils.toFormattedString
+import java.time.LocalTime
class ExamDialog : DialogFragment() {
@@ -54,6 +56,14 @@ class ExamDialog : DialogFragment() {
}
examDialogClose.setOnClickListener { dismiss() }
+ examDialogAddToCalendar.setOnClickListener {
+ requireContext().openCalendarEventAdd(
+ title = "${exam.subject} - ${exam.type}",
+ description = exam.description,
+ start = exam.date.atTime(LocalTime.of(8, 0)),
+ end = exam.date.atTime(LocalTime.of(8, 45)),
+ )
+ }
}
}
}
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 ecd982a18..323e1e477 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt
@@ -1,31 +1,17 @@
package io.github.wulkanowy.utils
import android.annotation.SuppressLint
-import android.content.ActivityNotFoundException
import android.content.Context
-import android.content.Intent
-import android.graphics.Bitmap
-import android.graphics.Color
-import android.graphics.Paint
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffColorFilter
-import android.graphics.Rect
-import android.graphics.Typeface
-import android.net.Uri
+import android.graphics.*
import android.text.TextPaint
import android.util.DisplayMetrics.DENSITY_DEFAULT
-import androidx.annotation.AttrRes
-import androidx.annotation.ColorInt
-import androidx.annotation.ColorRes
-import androidx.annotation.DrawableRes
-import androidx.annotation.PluralsRes
+import androidx.annotation.*
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.graphics.applyCanvas
import androidx.core.graphics.drawable.RoundedBitmapDrawable
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import androidx.core.graphics.drawable.toBitmap
-import io.github.wulkanowy.BuildConfig.APPLICATION_ID
@ColorInt
fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int {
@@ -61,69 +47,6 @@ fun Context.getCompatBitmap(@DrawableRes drawableRes: Int, @ColorRes colorRes: I
fun Context.getPlural(@PluralsRes pluralRes: Int, quantity: Int, vararg arguments: Any) =
resources.getQuantityString(pluralRes, quantity, *arguments)
-fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) -> Unit = {}) {
- Intent.parseUri(uri, 0).let {
- try {
- startActivity(it)
- } catch (e: ActivityNotFoundException) {
- onActivityNotFound(uri)
- }
- }
-}
-
-fun Context.openAppInMarket(onActivityNotFound: (uri: String) -> Unit) {
- openInternetBrowser("market://details?id=${APPLICATION_ID}") {
- openInternetBrowser("https://github.com/wulkanowy/wulkanowy/releases", onActivityNotFound)
- }
-}
-
-fun Context.openEmailClient(
- chooserTitle: String,
- email: String,
- subject: String,
- body: String,
- onActivityNotFound: () -> Unit = {}
-) {
- val intent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")).apply {
- putExtra(Intent.EXTRA_EMAIL, arrayOf(email))
- putExtra(Intent.EXTRA_SUBJECT, subject)
- putExtra(Intent.EXTRA_TEXT, body)
- }
-
- if (intent.resolveActivity(packageManager) != null) {
- startActivity(Intent.createChooser(intent, chooserTitle))
- } else onActivityNotFound()
-}
-
-fun Context.openNavigation(location: String) {
- val intentUri = Uri.parse("geo:0,0?q=${Uri.encode(location)}")
- val intent = Intent(Intent.ACTION_VIEW, intentUri)
- if (intent.resolveActivity(packageManager) != null) {
- startActivity(intent)
- }
-}
-
-fun Context.openDialer(phone: String) {
- val intentUri = Uri.parse("tel:$phone")
- val intent = Intent(Intent.ACTION_DIAL, intentUri)
- if (intent.resolveActivity(packageManager) != null) {
- 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
@SuppressLint("DefaultLocale")
diff --git a/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt
new file mode 100644
index 000000000..1ef03f2e6
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/utils/IntentUtils.kt
@@ -0,0 +1,100 @@
+package io.github.wulkanowy.utils
+
+import android.content.ActivityNotFoundException
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.provider.CalendarContract
+import io.github.wulkanowy.BuildConfig
+import java.time.LocalDateTime
+import java.time.ZoneId
+
+fun Context.openInternetBrowser(uri: String, onActivityNotFound: (uri: String) -> Unit = {}) {
+ Intent.parseUri(uri, 0).let {
+ try {
+ startActivity(it)
+ } catch (e: ActivityNotFoundException) {
+ onActivityNotFound(uri)
+ }
+ }
+}
+
+fun Context.openAppInMarket(onActivityNotFound: (uri: String) -> Unit) {
+ openInternetBrowser("market://details?id=${BuildConfig.APPLICATION_ID}") {
+ openInternetBrowser("https://github.com/wulkanowy/wulkanowy/releases", onActivityNotFound)
+ }
+}
+
+fun Context.openEmailClient(
+ chooserTitle: String,
+ email: String,
+ subject: String,
+ body: String,
+ onActivityNotFound: () -> Unit = {}
+) {
+ val intent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:")).apply {
+ putExtra(Intent.EXTRA_EMAIL, arrayOf(email))
+ putExtra(Intent.EXTRA_SUBJECT, subject)
+ putExtra(Intent.EXTRA_TEXT, body)
+ }
+
+ if (intent.resolveActivity(packageManager) != null) {
+ startActivity(Intent.createChooser(intent, chooserTitle))
+ } else onActivityNotFound()
+}
+
+fun Context.openCalendarEventAdd(
+ title: String,
+ description: String,
+ start: LocalDateTime,
+ end: LocalDateTime? = null,
+ isAllDay: Boolean = false,
+ onActivityNotFound: (uri: String?) -> Unit = {},
+) {
+ val beginTime = start.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()
+ val endTime = end?.atZone(ZoneId.systemDefault())?.toInstant()?.toEpochMilli()
+
+ val intent = Intent(Intent.ACTION_INSERT)
+ .setData(CalendarContract.Events.CONTENT_URI)
+ .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime)
+ .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime)
+ .putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay)
+ .putExtra(CalendarContract.Events.TITLE, title)
+ .putExtra(CalendarContract.Events.DESCRIPTION, description)
+ .putExtra(CalendarContract.Events.AVAILABILITY, CalendarContract.Events.AVAILABILITY_BUSY)
+
+ try {
+ startActivity(intent)
+ } catch (e: ActivityNotFoundException) {
+ onActivityNotFound(intent.dataString)
+ }
+}
+
+fun Context.openNavigation(location: String) {
+ val intentUri = Uri.parse("geo:0,0?q=${Uri.encode(location)}")
+ val intent = Intent(Intent.ACTION_VIEW, intentUri)
+ if (intent.resolveActivity(packageManager) != null) {
+ startActivity(intent)
+ }
+}
+
+fun Context.openDialer(phone: String) {
+ val intentUri = Uri.parse("tel:$phone")
+ val intent = Intent(Intent.ACTION_DIAL, intentUri)
+ if (intent.resolveActivity(packageManager) != null) {
+ 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)
+}
diff --git a/app/src/main/res/drawable/ic_calendat_all.xml b/app/src/main/res/drawable/ic_calendar_all.xml
similarity index 100%
rename from app/src/main/res/drawable/ic_calendat_all.xml
rename to app/src/main/res/drawable/ic_calendar_all.xml
diff --git a/app/src/main/res/layout/dialog_additional_add.xml b/app/src/main/res/layout/dialog_additional_add.xml
index 884018e55..54f031be9 100644
--- a/app/src/main/res/layout/dialog_additional_add.xml
+++ b/app/src/main/res/layout/dialog_additional_add.xml
@@ -33,7 +33,7 @@
android:layout_marginHorizontal="16dp"
android:layout_marginTop="28dp"
android:hint="@string/all_date"
- app:startIconDrawable="@drawable/ic_calendat_all">
+ app:startIconDrawable="@drawable/ic_calendar_all">
+
+
+ app:startIconDrawable="@drawable/ic_calendar_all">
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5712a6cc0..ee702251e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -637,6 +637,7 @@
Copied
Undo
Change
+ Add to calendar
From 26e0f43fa08899ffdf57086bca722302b6abf992 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 17 Mar 2022 18:28:26 +0000
Subject: [PATCH 074/669] Bump firebase-bom from 29.1.0 to 29.2.0 (#1803)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 8467dd018..f2a578c8e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -233,7 +233,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
- playImplementation platform('com.google.firebase:firebase-bom:29.1.0')
+ playImplementation platform('com.google.firebase:firebase-bom:29.2.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 5ce30a300010b9d1445361a924dd89851cbd0d2c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 20 Mar 2022 00:25:11 +0000
Subject: [PATCH 075/669] Bump firebase-bom from 29.2.0 to 29.2.1 (#1804)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index f2a578c8e..86f7ee169 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -233,7 +233,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
- playImplementation platform('com.google.firebase:firebase-bom:29.2.0')
+ playImplementation platform('com.google.firebase:firebase-bom:29.2.1')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From c8d069c7875a64e4a00e98eb18e641dba45c6654 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 20 Mar 2022 00:25:31 +0000
Subject: [PATCH 076/669] Bump hianalytics from 6.4.0.300 to 6.4.1.300 (#1805)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 86f7ee169..12698a085 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
playImplementation 'com.google.android.play:core-ktx:1.8.1'
playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
- hmsImplementation 'com.huawei.hms:hianalytics:6.4.0.300'
+ hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.4.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From f851a4d2c596095f80a6f34607e4be7af5214143 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Mar 2022 12:47:27 -0500
Subject: [PATCH 077/669] Bump huawei-publish-gradle-plugin from 1.3.1 to 1.3.3
(#1806)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 1a97f58ce..6b8d14ed9 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,7 +19,7 @@ buildscript {
classpath 'com.huawei.agconnect:agcp:1.6.4.300'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
- classpath "ru.cian:huawei-publish-gradle-plugin:1.3.1"
+ classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3"
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries"
From a07741b5c58767ed7c1e2c20043d45a654661c3d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Mar 2022 17:49:32 +0000
Subject: [PATCH 078/669] Bump WhatTheStack from 1.0.0-alpha03 to 1.0.0-alpha04
(#1807)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 12698a085..d4e890843 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -248,7 +248,7 @@ dependencies {
debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker"
debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.6'
- debugImplementation 'com.github.haroldadmin:WhatTheStack:1.0.0-alpha03'
+ debugImplementation 'com.github.haroldadmin:WhatTheStack:1.0.0-alpha04'
testImplementation "junit:junit:4.13.2"
testImplementation "io.mockk:mockk:$mockk"
From 8d8990761a0755ce4e8c53c7a3405cd2552a25fb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 26 Mar 2022 22:37:22 +0000
Subject: [PATCH 079/669] Bump firebase-bom from 29.2.1 to 29.3.0 (#1809)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index d4e890843..73948bad5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -233,7 +233,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
- playImplementation platform('com.google.firebase:firebase-bom:29.2.1')
+ playImplementation platform('com.google.firebase:firebase-bom:29.3.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 042b66ca5cf45650fddc42523536317e8332b67d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 26 Mar 2022 22:37:53 +0000
Subject: [PATCH 080/669] Bump core-splashscreen from 1.0.0-beta01 to
1.0.0-beta02 (#1810)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 73948bad5..7ae210675 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -186,7 +186,7 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
implementation "androidx.core:core-ktx:1.7.0"
- implementation 'androidx.core:core-splashscreen:1.0.0-beta01'
+ implementation 'androidx.core:core-splashscreen:1.0.0-beta02'
implementation "androidx.activity:activity-ktx:1.4.0"
implementation "androidx.appcompat:appcompat:1.4.1"
implementation "androidx.fragment:fragment-ktx:1.4.1"
From 20dde6e89605495f1481d0bf588cba1f3dd00878 Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Sun, 27 Mar 2022 15:33:32 +0200
Subject: [PATCH 081/669] Resource refactor (#1589)
---
.../java/io/github/wulkanowy/data/Resource.kt | 180 ++++++-
.../wulkanowy/data/db/dao/StudentDao.kt | 4 +
.../repositories/AdminMessageRepository.kt | 3 +-
.../data/repositories/AttendanceRepository.kt | 2 +
.../AttendanceSummaryRepository.kt | 7 +-
.../CompletedLessonsRepository.kt | 2 +
.../data/repositories/ConferenceRepository.kt | 7 +-
.../data/repositories/ExamRepository.kt | 2 +
.../data/repositories/GradeRepository.kt | 5 +
.../repositories/GradeStatisticsRepository.kt | 9 +-
.../data/repositories/HomeworkRepository.kt | 2 +
.../repositories/LuckyNumberRepository.kt | 3 +-
.../data/repositories/MessageRepository.kt | 14 +-
.../repositories/MobileDeviceRepository.kt | 7 +-
.../data/repositories/NoteRepository.kt | 2 +
.../SchoolAnnouncementRepository.kt | 3 +-
.../data/repositories/SchoolRepository.kt | 3 +-
.../repositories/StudentInfoRepository.kt | 3 +-
.../data/repositories/StudentRepository.kt | 9 +
.../data/repositories/SubjectRepository.kt | 7 +-
.../data/repositories/TeacherRepository.kt | 7 +-
.../data/repositories/TimetableRepository.kt | 14 +-
.../alarm/TimetableNotificationReceiver.kt | 13 +-
.../sync/works/AttendanceSummaryWork.kt | 2 +-
.../services/sync/works/AttendanceWork.kt | 2 +-
.../sync/works/CompletedLessonWork.kt | 2 +-
.../services/sync/works/ConferenceWork.kt | 2 +-
.../wulkanowy/services/sync/works/ExamWork.kt | 2 +-
.../sync/works/GradeStatisticsWork.kt | 3 +-
.../services/sync/works/GradeWork.kt | 2 +-
.../services/sync/works/HomeworkWork.kt | 2 +-
.../services/sync/works/LuckyNumberWork.kt | 2 +-
.../services/sync/works/MessageWork.kt | 2 +-
.../wulkanowy/services/sync/works/NoteWork.kt | 2 +-
.../sync/works/SchoolAnnouncementWork.kt | 2 +-
.../services/sync/works/TeacherWork.kt | 3 +-
.../services/sync/works/TimetableWork.kt | 2 +-
.../github/wulkanowy/ui/base/BasePresenter.kt | 43 +-
.../about/contributor/ContributorPresenter.kt | 20 +-
.../modules/about/license/LicensePresenter.kt | 26 +-
.../ui/modules/account/AccountPresenter.kt | 25 +-
.../accountdetails/AccountDetailsPresenter.kt | 110 ++--
.../accountedit/AccountEditPresenter.kt | 52 +-
.../accountquick/AccountQuickPresenter.kt | 25 +-
.../modules/attendance/AttendancePresenter.kt | 135 ++---
.../summary/AttendanceSummaryFragment.kt | 2 +-
.../summary/AttendanceSummaryPresenter.kt | 102 ++--
.../summary/AttendanceSummaryView.kt | 2 +-
.../modules/conference/ConferencePresenter.kt | 72 +--
.../ui/modules/dashboard/DashboardFragment.kt | 2 +-
.../modules/dashboard/DashboardPresenter.kt | 389 +++++++-------
.../debug/logviewer/LogViewerPresenter.kt | 52 +-
.../ui/modules/exam/ExamPresenter.kt | 108 ++--
.../ui/modules/grade/GradeAverageProvider.kt | 33 +-
.../ui/modules/grade/GradePresenter.kt | 46 +-
.../grade/details/GradeDetailsDialog.kt | 1 +
.../grade/details/GradeDetailsPresenter.kt | 132 ++---
.../statistics/GradeStatisticsPresenter.kt | 134 ++---
.../grade/summary/GradeSummaryPresenter.kt | 85 ++-
.../ui/modules/homework/HomeworkPresenter.kt | 113 ++--
.../homework/add/HomeworkAddPresenter.kt | 31 +-
.../details/HomeworkDetailsPresenter.kt | 49 +-
.../login/advanced/LoginAdvancedPresenter.kt | 62 +--
.../modules/login/form/LoginFormPresenter.kt | 75 ++-
.../login/recover/LoginRecoverPresenter.kt | 61 ++-
.../LoginStudentSelectPresenter.kt | 35 +-
.../login/symbol/LoginSymbolPresenter.kt | 24 +-
.../luckynumber/LuckyNumberPresenter.kt | 71 ++-
.../history/LuckyNumberHistoryPresenter.kt | 102 ++--
.../LuckyNumberWidgetConfigurePresenter.kt | 17 +-
.../LuckyNumberWidgetProvider.kt | 20 +-
.../ui/modules/main/MainPresenter.kt | 29 +-
.../preview/MessagePreviewPresenter.kt | 104 ++--
.../message/send/SendMessagePresenter.kt | 117 +++--
.../message/tab/MessageTabPresenter.kt | 116 ++--
.../mobiledevice/MobileDevicePresenter.kt | 94 ++--
.../token/MobileDeviceTokenPresenter.kt | 43 +-
.../ui/modules/note/NotePresenter.kt | 84 ++-
.../school/SchoolPresenter.kt | 53 +-
.../teacher/TeacherPresenter.kt | 65 ++-
.../SchoolAnnouncementPresenter.kt | 70 +--
.../studentinfo/StudentInfoPresenter.kt | 72 ++-
.../modules/timetable/TimetablePresenter.kt | 80 ++-
.../additional/AdditionalLessonsPresenter.kt | 74 ++-
.../completed/CompletedLessonsPresenter.kt | 88 ++--
.../TimetableWidgetConfigurePresenter.kt | 17 +-
.../timetablewidget/TimetableWidgetFactory.kt | 5 +-
.../io/github/wulkanowy/utils/FlowUtils.kt | 96 ----
.../ResourceTest.kt} | 8 +-
.../repositories/AttendanceRepositoryTest.kt | 23 +-
.../CompletedLessonsRepositoryTest.kt | 23 +-
.../data/repositories/ExamRemoteTest.kt | 23 +-
.../data/repositories/GradeRepositoryTest.kt | 28 +-
.../GradeStatisticsRepositoryTest.kt | 16 +-
.../repositories/LuckyNumberRemoteTest.kt | 22 +-
.../repositories/MessageRepositoryTest.kt | 19 +-
.../MobileDeviceRepositoryTest.kt | 23 +-
.../repositories/TimetableRepositoryTest.kt | 15 +-
.../modules/grade/GradeAverageProviderTest.kt | 494 +++++++++++++-----
.../github/wulkanowy/utils/ResourceUtils.kt | 14 +
gradle/wrapper/gradle-wrapper.properties | 2 +-
gradlew | 269 ++++++----
102 files changed, 2419 insertions(+), 2361 deletions(-)
delete mode 100644 app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt
rename app/src/test/java/io/github/wulkanowy/{utils/FlowUtilsKtTest.kt => data/ResourceTest.kt} (96%)
create mode 100644 app/src/test/java/io/github/wulkanowy/utils/ResourceUtils.kt
diff --git a/app/src/main/java/io/github/wulkanowy/data/Resource.kt b/app/src/main/java/io/github/wulkanowy/data/Resource.kt
index 406440c83..44f8a1b48 100644
--- a/app/src/main/java/io/github/wulkanowy/data/Resource.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/Resource.kt
@@ -1,23 +1,173 @@
package io.github.wulkanowy.data
-data class Resource(val status: Status, val data: T?, val error: Throwable?) {
- companion object {
- fun success(data: T?): Resource {
- return Resource(Status.SUCCESS, data, null)
- }
+import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
+import timber.log.Timber
- fun error(error: Throwable?, data: T? = null): Resource {
- return Resource(Status.ERROR, data, error)
- }
+sealed class Resource {
- fun loading(data: T? = null): Resource {
- return Resource(Status.LOADING, data, null)
- }
+ open class Loading : Resource()
+
+ data class Intermediate(val data: T) : Loading()
+
+ data class Success(val data: T) : Resource()
+
+ data class Error(val error: Throwable) : Resource()
+}
+
+val Resource.dataOrNull: T?
+ get() = when (this) {
+ is Resource.Success -> this.data
+ is Resource.Intermediate -> this.data
+ is Resource.Loading -> null
+ is Resource.Error -> null
+ }
+
+val Resource.errorOrNull: Throwable?
+ get() = when (this) {
+ is Resource.Error -> this.error
+ else -> null
+ }
+
+fun resourceFlow(block: suspend () -> T) = flow {
+ emit(Resource.Loading())
+ emit(Resource.Success(block()))
+}.catch { emit(Resource.Error(it)) }
+
+fun flatResourceFlow(block: suspend () -> Flow>) = flow {
+ emit(Resource.Loading())
+ emitAll(block().filter { it is Resource.Intermediate || it !is Resource.Loading })
+}.catch { emit(Resource.Error(it)) }
+
+fun Resource.mapData(block: (T) -> U) = when (this) {
+ is Resource.Success -> Resource.Success(block(this.data))
+ is Resource.Intermediate -> Resource.Intermediate(block(this.data))
+ is Resource.Loading -> Resource.Loading()
+ is Resource.Error -> Resource.Error(this.error)
+}
+
+fun Flow>.logResourceStatus(name: String, showData: Boolean = false) = onEach {
+ val description = when (it) {
+ is Resource.Loading -> "started"
+ is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
+ is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else ""
+ is Resource.Error -> "exception occurred: ${it.error}"
+ }
+ Timber.i("$name: $description")
+}
+
+fun Flow>.mapResourceData(block: (T) -> U) = map {
+ it.mapData(block)
+}
+
+fun Flow>.onResourceData(block: suspend (T) -> Unit) = onEach {
+ when (it) {
+ is Resource.Success -> block(it.data)
+ is Resource.Intermediate -> block(it.data)
+ is Resource.Error,
+ is Resource.Loading -> Unit
}
}
-enum class Status {
- LOADING,
- SUCCESS,
- ERROR
+fun Flow>.onResourceLoading(block: suspend () -> Unit) = onEach {
+ if (it is Resource.Loading) {
+ block()
+ }
+}
+
+fun Flow>.onResourceIntermediate(block: suspend (T) -> Unit) = onEach {
+ if (it is Resource.Intermediate) {
+ block(it.data)
+ }
+}
+
+fun Flow>.onResourceSuccess(block: suspend (T) -> Unit) = onEach {
+ if (it is Resource.Success) {
+ block(it.data)
+ }
+}
+
+fun Flow>.onResourceError(block: (Throwable) -> Unit) = onEach {
+ if (it is Resource.Error) {
+ block(it.error)
+ }
+}
+
+fun Flow>.onResourceNotLoading(block: () -> Unit) = onEach {
+ if (it !is Resource.Loading) {
+ block()
+ }
+}
+
+suspend fun Flow>.toFirstResult() = filter { it !is Resource.Loading }.first()
+
+suspend fun Flow>.waitForResult() = takeWhile { it is Resource.Loading }.collect()
+
+inline fun networkBoundResource(
+ mutex: Mutex = Mutex(),
+ showSavedOnLoading: Boolean = true,
+ crossinline isResultEmpty: (ResultType) -> Boolean,
+ crossinline query: () -> Flow,
+ crossinline fetch: suspend (ResultType) -> RequestType,
+ crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit,
+ crossinline onFetchFailed: (Throwable) -> Unit = { },
+ crossinline shouldFetch: (ResultType) -> Boolean = { true },
+ crossinline filterResult: (ResultType) -> ResultType = { it }
+) = flow {
+ emit(Resource.Loading())
+
+ val data = query().first()
+ emitAll(if (shouldFetch(data)) {
+ val filteredResult = filterResult(data)
+
+ if (showSavedOnLoading && !isResultEmpty(filteredResult)) {
+ emit(Resource.Intermediate(filteredResult))
+ }
+
+ try {
+ val newData = fetch(data)
+ mutex.withLock { saveFetchResult(query().first(), newData) }
+ query().map { Resource.Success(filterResult(it)) }
+ } catch (throwable: Throwable) {
+ onFetchFailed(throwable)
+ query().map { Resource.Error(throwable) }
+ }
+ } else {
+ query().map { Resource.Success(filterResult(it)) }
+ })
+}
+
+@JvmName("networkBoundResourceWithMap")
+inline fun networkBoundResource(
+ mutex: Mutex = Mutex(),
+ showSavedOnLoading: Boolean = true,
+ crossinline isResultEmpty: (T) -> Boolean,
+ crossinline query: () -> Flow,
+ crossinline fetch: suspend (ResultType) -> RequestType,
+ crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit,
+ crossinline onFetchFailed: (Throwable) -> Unit = { },
+ crossinline shouldFetch: (ResultType) -> Boolean = { true },
+ crossinline mapResult: (ResultType) -> T
+) = flow {
+ emit(Resource.Loading())
+
+ val data = query().first()
+ emitAll(if (shouldFetch(data)) {
+ val mappedResult = mapResult(data)
+
+ if (showSavedOnLoading && !isResultEmpty(mappedResult)) {
+ emit(Resource.Intermediate(mappedResult))
+ }
+ try {
+ val newData = fetch(data)
+ mutex.withLock { saveFetchResult(query().first(), newData) }
+ query().map { Resource.Success(mapResult(it)) }
+ } catch (throwable: Throwable) {
+ onFetchFailed(throwable)
+ query().map { Resource.Error(throwable) }
+ }
+ } else {
+ query().map { Resource.Success(mapResult(it)) }
+ })
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
index 853a7cb74..87b3e0b32 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/StudentDao.kt
@@ -33,6 +33,10 @@ abstract class StudentDao {
@Query("SELECT * FROM Students")
abstract suspend fun loadStudentsWithSemesters(): List
+ @Transaction
+ @Query("SELECT * FROM Students WHERE id = :id")
+ abstract suspend fun loadStudentWithSemestersById(id: Long): StudentWithSemesters?
+
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
abstract suspend fun updateCurrent(id: Long)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt
index e455411ea..c9655b722 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt
@@ -3,8 +3,8 @@ package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.api.AdminMessageService
import io.github.wulkanowy.data.db.dao.AdminMessageDao
import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.utils.AppInfo
-import io.github.wulkanowy.utils.networkBoundResource
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -19,6 +19,7 @@ class AdminMessageRepository @Inject constructor(
suspend fun getAdminMessages(student: Student) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it == null },
query = { adminMessageDao.loadAll() },
fetch = { adminMessageService.getAdminMessages() },
shouldFetch = { true },
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
index 7184f5576..9aa6562a6 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
@@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Absent
import io.github.wulkanowy.utils.*
@@ -36,6 +37,7 @@ class AttendanceRepository @Inject constructor(
notify: Boolean = false,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(
key = getRefreshKey(cacheKey, semester, start, end)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
index 0857475f8..8e0709135 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
@@ -4,8 +4,12 @@ import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.*
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -28,6 +32,7 @@ class AttendanceSummaryRepository @Inject constructor(
forceRefresh: Boolean,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
it.isEmpty() || forceRefresh || isExpired
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
index 2055f3f47..8f393cadb 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
@@ -4,6 +4,7 @@ import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.*
import kotlinx.coroutines.sync.Mutex
@@ -30,6 +31,7 @@ class CompletedLessonsRepository @Inject constructor(
forceRefresh: Boolean,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(
key = getRefreshKey(cacheKey, semester, start, end)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
index 6af24d73f..83204cab0 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
@@ -5,8 +5,12 @@ import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.*
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import java.time.Instant
@@ -32,6 +36,7 @@ class ConferenceRepository @Inject constructor(
startDate: Instant = Instant.EPOCH,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
it.isEmpty() || forceRefresh || isExpired
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
index c655c8001..faa80b93e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
@@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
@@ -33,6 +34,7 @@ class ExamRepository @Inject constructor(
notify: Boolean = false,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(
key = getRefreshKey(cacheKey, semester, start, end)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
index f4087a887..f5f895d82 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
@@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
@@ -36,6 +37,10 @@ class GradeRepository @Inject constructor(
notify: Boolean = false,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = {
+ //When details is empty and summary is not, app will not use summary cache - edge case
+ it.first.isEmpty()
+ },
shouldFetch = { (details, summaries) ->
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
details.isEmpty() || summaries.isEmpty() || forceRefresh || isExpired
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
index 4d26c3126..9fa06c497 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
@@ -11,8 +11,12 @@ import io.github.wulkanowy.data.mappers.mapPartialToStatisticItems
import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.*
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.sync.Mutex
import java.util.*
import javax.inject.Inject
@@ -42,6 +46,7 @@ class GradeStatisticsRepository @Inject constructor(
forceRefresh: Boolean,
) = networkBoundResource(
mutex = partialMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(
key = getRefreshKey(partialCacheKey, semester)
@@ -86,6 +91,7 @@ class GradeStatisticsRepository @Inject constructor(
forceRefresh: Boolean,
) = networkBoundResource(
mutex = semesterMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(
key = getRefreshKey(semesterCacheKey, semester)
@@ -143,6 +149,7 @@ class GradeStatisticsRepository @Inject constructor(
forceRefresh: Boolean,
) = networkBoundResource(
mutex = pointsMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(pointsCacheKey, semester))
it.isEmpty() || forceRefresh || isExpired
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
index 900d9a68e..f564824de 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
@@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.*
import kotlinx.coroutines.sync.Mutex
@@ -32,6 +33,7 @@ class HomeworkRepository @Inject constructor(
notify: Boolean = false,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(
key = getRefreshKey(cacheKey, semester, start, end)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt
index 41e824e57..87e8410f1 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt
@@ -4,9 +4,9 @@ import io.github.wulkanowy.data.db.dao.LuckyNumberDao
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntity
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
@@ -29,6 +29,7 @@ class LuckyNumberRepository @Inject constructor(
notify: Boolean = false,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it == null },
shouldFetch = { it == null || forceRefresh },
query = { luckyNumberDb.load(student.studentId, now()) },
fetch = {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
index 224c69bd7..8d6fd772a 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
@@ -7,15 +7,12 @@ import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
import io.github.wulkanowy.data.db.dao.MessagesDao
-import io.github.wulkanowy.data.db.entities.Message
-import io.github.wulkanowy.data.db.entities.MessageWithAttachment
-import io.github.wulkanowy.data.db.entities.Recipient
-import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
import io.github.wulkanowy.data.mappers.mapFromEntities
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.data.pojos.MessageDraft
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Folder
@@ -23,7 +20,6 @@ import io.github.wulkanowy.sdk.pojo.SentMessage
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
@@ -59,6 +55,7 @@ class MessageRepository @Inject constructor(
notify: Boolean = false,
): Flow>> = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(
key = getRefreshKey(cacheKey, student, folder)
@@ -106,8 +103,9 @@ class MessageRepository @Inject constructor(
message: Message,
markAsRead: Boolean = false,
): Flow> = networkBoundResource(
+ isResultEmpty = { it == null },
shouldFetch = {
- checkNotNull(it, { "This message no longer exist!" })
+ checkNotNull(it) { "This message no longer exist!" }
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
it.message.unread || it.message.content.isEmpty()
},
@@ -123,7 +121,7 @@ class MessageRepository @Inject constructor(
}
},
saveFetchResult = { old, (downloadedMessage, attachments) ->
- checkNotNull(old, { "Fetched message no longer exist!" })
+ checkNotNull(old) { "Fetched message no longer exist!" }
messagesDb.updateAll(listOf(old.message.apply {
id = old.message.id
unread = !markAsRead
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
index f825c36df..eda40cac4 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
@@ -6,9 +6,13 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.*
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -30,6 +34,7 @@ class MobileDeviceRepository @Inject constructor(
forceRefresh: Boolean,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
it.isEmpty() || forceRefresh || isExpired
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
index 19ad8f037..e5d7bc5cb 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
@@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
@@ -30,6 +31,7 @@ class NoteRepository @Inject constructor(
notify: Boolean = false,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(
getRefreshKey(cacheKey, semester)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
index b6724ed34..cf7ac86cd 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
@@ -4,11 +4,11 @@ import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
@@ -31,6 +31,7 @@ class SchoolAnnouncementRepository @Inject constructor(
forceRefresh: Boolean, notify: Boolean = false
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
it.isEmpty() || forceRefresh || isExpired
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
index 880a6a74c..7972ed084 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
@@ -4,11 +4,11 @@ import io.github.wulkanowy.data.db.dao.SchoolDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntity
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -30,6 +30,7 @@ class SchoolRepository @Inject constructor(
forceRefresh: Boolean,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it == null },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(
key = getRefreshKey(cacheKey, student)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
index 1fa91dd46..efc82a772 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
@@ -4,9 +4,9 @@ import io.github.wulkanowy.data.db.dao.StudentInfoDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntity
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.networkBoundResource
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -25,6 +25,7 @@ class StudentInfoRepository @Inject constructor(
forceRefresh: Boolean,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it == null },
shouldFetch = { it == null || forceRefresh },
query = { studentInfoDao.loadStudentInfo(student.studentId) },
fetch = {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt
index 570f8bdb9..f006b7d28 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt
@@ -73,6 +73,15 @@ class StudentRepository @Inject constructor(
}
}
+ suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true) =
+ studentDb.loadStudentWithSemestersById(id)?.apply {
+ if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
+ student.password = withContext(dispatchers.io) {
+ decrypt(student.password)
+ }
+ }
+ }
+
suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student {
val student = studentDb.loadById(id) ?: throw NoCurrentStudentException()
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
index b9bca028f..3926122b3 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
@@ -4,8 +4,12 @@ import io.github.wulkanowy.data.db.dao.SubjectDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.*
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -27,6 +31,7 @@ class SubjectRepository @Inject constructor(
forceRefresh: Boolean = false,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
it.isEmpty() || forceRefresh || isExpired
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
index 6b615c7a7..acd71e1f4 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
@@ -4,8 +4,12 @@ import io.github.wulkanowy.data.db.dao.TeacherDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.*
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@@ -27,6 +31,7 @@ class TeacherRepository @Inject constructor(
forceRefresh: Boolean,
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
it.isEmpty() || forceRefresh || isExpired
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
index 7534640c3..3145c2a23 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
@@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.data.pojos.TimetableFull
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
@@ -30,6 +31,10 @@ class TimetableRepository @Inject constructor(
private val cacheKey = "timetable"
+ enum class TimetableType {
+ NORMAL, ADDITIONAL
+ }
+
fun getTimetable(
student: Student,
semester: Semester,
@@ -37,9 +42,16 @@ class TimetableRepository @Inject constructor(
end: LocalDate,
forceRefresh: Boolean,
refreshAdditional: Boolean = false,
- notify: Boolean = false
+ notify: Boolean = false,
+ timetableType: TimetableType = TimetableType.NORMAL
) = networkBoundResource(
mutex = saveFetchResultMutex,
+ isResultEmpty = {
+ when (timetableType) {
+ TimetableType.NORMAL -> it.lessons.isEmpty()
+ TimetableType.ADDITIONAL -> it.additional.isEmpty()
+ }
+ },
shouldFetch = { (timetable, additional, headers) ->
val refreshKey = getRefreshKey(cacheKey, semester, start, end)
val isExpired = refreshHelper.shouldBeRefreshed(refreshKey)
diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
index c3ff1838e..01a583e13 100644
--- a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationReceiver.kt
@@ -10,19 +10,18 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.PendingIntentCompat
-import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.getCompatColor
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -59,7 +58,7 @@ class TimetableNotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Timber.d("Receiving intent... ${intent.toUri(0)}")
- flowWithResource {
+ resourceFlow {
val showStudentName = !studentRepository.isOneUniqueStudent()
val student = studentRepository.getCurrentStudent(false)
val studentId = intent.getIntExtra(STUDENT_ID, 0)
@@ -69,9 +68,9 @@ class TimetableNotificationReceiver : BroadcastReceiver() {
} else {
Timber.d("Notification studentId($studentId) differs from current(${student.studentId})")
}
- }.onEach {
- if (it.status == Status.ERROR) Timber.e(it.error!!)
- }.launchIn(GlobalScope)
+ }
+ .onResourceError { Timber.e(it) }
+ .launchIn(GlobalScope)
}
private fun prepareNotification(context: Context, intent: Intent, showStudentName: Boolean) {
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt
index 84b7017b1..55ce7e908 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceSummaryWork.kt
@@ -3,7 +3,7 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
-import io.github.wulkanowy.utils.waitForResult
+import io.github.wulkanowy.data.waitForResult
import javax.inject.Inject
class AttendanceSummaryWork @Inject constructor(
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt
index 9abf43e08..657f69638 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/AttendanceWork.kt
@@ -3,9 +3,9 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.AttendanceRepository
+import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification
import io.github.wulkanowy.utils.previousOrSameSchoolDay
-import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import java.time.LocalDate.now
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt
index c6ada9446..f898aa04b 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/CompletedLessonWork.kt
@@ -3,9 +3,9 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.CompletedLessonsRepository
+import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.sunday
-import io.github.wulkanowy.utils.waitForResult
import java.time.LocalDate.now
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt
index becd74668..c85c00433 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ConferenceWork.kt
@@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.ConferenceRepository
+import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewConferenceNotification
-import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt
index 39579dc8c..7071bce20 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt
@@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.ExamRepository
+import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewExamNotification
-import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import java.time.LocalDate.now
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt
index 2e915199e..ac35bc9a8 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeStatisticsWork.kt
@@ -3,7 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.GradeStatisticsRepository
-import io.github.wulkanowy.utils.waitForResult
+import io.github.wulkanowy.data.waitForResult
+
import javax.inject.Inject
class GradeStatisticsWork @Inject constructor(
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt
index dd49f143c..ba21b8600 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/GradeWork.kt
@@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.GradeRepository
+import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewGradeNotification
-import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt
index 1385191b7..4cfe27d0d 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/HomeworkWork.kt
@@ -3,9 +3,9 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.HomeworkRepository
+import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewHomeworkNotification
import io.github.wulkanowy.utils.nextOrSameSchoolDay
-import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import java.time.LocalDate.now
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt
index f223a8546..668b1b6b8 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/LuckyNumberWork.kt
@@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
+import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewLuckyNumberNotification
-import io.github.wulkanowy.utils.waitForResult
import javax.inject.Inject
class LuckyNumberWork @Inject constructor(
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 5bf326c7b..26fac1a2f 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
@@ -4,8 +4,8 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
import io.github.wulkanowy.data.repositories.MessageRepository
+import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewMessageNotification
-import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt
index d66c3d661..df6e2b06b 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/NoteWork.kt
@@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.NoteRepository
+import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewNoteNotification
-import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt
index 9cee59024..805ceb3e4 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt
@@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
+import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification
-import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt
index 751fb6cc7..e7c72bf00 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TeacherWork.kt
@@ -3,7 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.TeacherRepository
-import io.github.wulkanowy.utils.waitForResult
+import io.github.wulkanowy.data.waitForResult
+
import javax.inject.Inject
class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work {
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt
index 575f9b961..29b1f13c7 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt
@@ -3,9 +3,9 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.TimetableRepository
+import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification
import io.github.wulkanowy.utils.nextOrSameSchoolDay
-import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import java.time.LocalDate.now
import javax.inject.Inject
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt
index 5cd5d0109..15c069f54 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/BasePresenter.kt
@@ -1,17 +1,10 @@
package io.github.wulkanowy.ui.base
-import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.StudentRepository
-import io.github.wulkanowy.utils.flowWithResource
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
open class BasePresenter(
@@ -37,28 +30,28 @@ open class BasePresenter(
}
fun onExpiredLoginSelected() {
- flowWithResource {
- val student = studentRepository.getCurrentStudent(false)
- studentRepository.logoutStudent(student)
+ Timber.i("Attempt to switch the student after the session expires")
- val students = studentRepository.getSavedStudents(false)
- if (students.isNotEmpty()) {
- Timber.i("Switching current student")
- studentRepository.switchStudent(students[0])
+ presenterScope.launch {
+ runCatching {
+ val student = studentRepository.getCurrentStudent(false)
+ studentRepository.logoutStudent(student)
+
+ val students = studentRepository.getSavedStudents(false)
+ if (students.isNotEmpty()) {
+ Timber.i("Switching current student")
+ studentRepository.switchStudent(students[0])
+ }
}
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Attempt to switch the student after the session expires")
- Status.SUCCESS -> {
+ .onFailure {
+ Timber.i("Switch student result: An exception occurred")
+ errorHandler.dispatch(it)
+ }
+ .onSuccess {
Timber.i("Switch student result: Open login view")
view?.openClearLoginView()
}
- Status.ERROR -> {
- Timber.i("Switch student result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
- }
- }.launch("expired")
+ }
}
fun Flow.launch(individualJobTag: String = "load"): Job {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt
index ef4b540e6..126bb2b48 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/contributor/ContributorPresenter.kt
@@ -1,13 +1,11 @@
package io.github.wulkanowy.ui.modules.about.contributor
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.pojos.Contributor
import io.github.wulkanowy.data.repositories.AppCreatorRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.utils.flowWithResource
-import kotlinx.coroutines.flow.onEach
import javax.inject.Inject
class ContributorPresenter @Inject constructor(
@@ -31,15 +29,11 @@ class ContributorPresenter @Inject constructor(
}
private fun loadData() {
- flowWithResource { appCreatorRepository.getAppCreators() }.onEach {
- when (it.status) {
- Status.LOADING -> view?.showProgress(true)
- Status.SUCCESS -> view?.run {
- showProgress(false)
- updateData(it.data!!)
- }
- Status.ERROR -> errorHandler.dispatch(it.error!!)
- }
- }.launch()
+ resourceFlow { appCreatorRepository.getAppCreators() }
+ .onResourceLoading { view?.showProgress(true) }
+ .onResourceSuccess { view?.updateData(it) }
+ .onResourceNotLoading { view?.showProgress(false) }
+ .onResourceError { errorHandler.dispatch(it) }
+ .launch()
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt
index 5368cc19d..5aa12a575 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt
@@ -1,16 +1,12 @@
package io.github.wulkanowy.ui.modules.about.license
import com.mikepenz.aboutlibraries.entity.Library
-import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.DispatchersProvider
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import timber.log.Timber
import javax.inject.Inject
class LicensePresenter @Inject constructor(
@@ -30,18 +26,16 @@ class LicensePresenter @Inject constructor(
}
private fun loadData() {
- flowWithResource {
- withContext(dispatchers.io) {
- view?.appLibraries.orEmpty()
+ presenterScope.launch {
+ runCatching {
+ withContext(dispatchers.io) {
+ view?.appLibraries.orEmpty()
+ }
}
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.d("License data load started")
- Status.SUCCESS -> view?.updateData(it.data!!)
- Status.ERROR -> errorHandler.dispatch(it.error!!)
- }
- }.afterLoading {
+ .onFailure { errorHandler.dispatch(it) }
+ .onSuccess { view?.updateData(it) }
+
view?.showProgress(false)
- }.launch()
+ }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
index 7fe77ca7a..77c1ffe64 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountPresenter.kt
@@ -1,12 +1,13 @@
package io.github.wulkanowy.ui.modules.account
-import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.data.logResourceStatus
+import io.github.wulkanowy.data.onResourceError
+import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.utils.flowWithResource
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -32,20 +33,10 @@ class AccountPresenter @Inject constructor(
}
private fun loadData() {
- flowWithResource { studentRepository.getSavedStudents(false) }
- .onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading account data started")
- Status.SUCCESS -> {
- Timber.i("Loading account result: Success")
- view?.updateData(createAccountItems(it.data!!))
- }
- Status.ERROR -> {
- Timber.i("Loading account result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
- }
- }
+ resourceFlow { studentRepository.getSavedStudents(false) }
+ .logResourceStatus("load account data")
+ .onResourceSuccess { view?.updateData(createAccountItems(it)) }
+ .onResourceError(errorHandler::dispatch)
.launch("load")
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt
index 1f44cbbc3..5d68ff2e1 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsPresenter.kt
@@ -1,7 +1,6 @@
package io.github.wulkanowy.ui.modules.account.accountdetails
-import io.github.wulkanowy.data.Resource
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentRepository
@@ -9,10 +8,6 @@ import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -51,40 +46,25 @@ class AccountDetailsPresenter @Inject constructor(
}
private fun loadData() {
- flowWithResource { studentRepository.getSavedStudents() }
- .map { studentWithSemesters ->
- Resource(
- data = studentWithSemesters.data?.single { it.student.id == studentId },
- status = studentWithSemesters.status,
- error = studentWithSemesters.error
- )
- }
- .onEach {
- when (it.status) {
- Status.LOADING -> {
- view?.run {
- showProgress(true)
- showContent(false)
- }
- Timber.i("Loading account details view started")
- }
- Status.SUCCESS -> {
- Timber.i("Loading account details view result: Success")
- studentWithSemesters = it.data
- view?.run {
- showAccountData(studentWithSemesters!!.student)
- enableSelectStudentButton(!studentWithSemesters!!.student.isCurrent)
- showContent(true)
- showErrorView(false)
- }
- }
- Status.ERROR -> {
- Timber.i("Loading account details view result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
+ resourceFlow { studentRepository.getSavedStudentById(studentId ?: -1) }
+ .logResourceStatus("loading account details view")
+ .onResourceLoading {
+ view?.run {
+ showProgress(true)
+ showContent(false)
}
}
- .afterLoading { view?.showProgress(false) }
+ .onResourceSuccess {
+ studentWithSemesters = it
+ view?.run {
+ showAccountData(studentWithSemesters!!.student)
+ enableSelectStudentButton(!studentWithSemesters!!.student.isCurrent)
+ showContent(true)
+ showErrorView(false)
+ }
+ }
+ .onResourceNotLoading { view?.showProgress(false) }
+ .onResourceError(errorHandler::dispatch)
.launch()
}
@@ -105,22 +85,12 @@ class AccountDetailsPresenter @Inject constructor(
Timber.i("Select student ${studentWithSemesters!!.student.id}")
- flowWithResource { studentRepository.switchStudent(studentWithSemesters!!) }
- .onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Attempt to change a student")
- Status.SUCCESS -> {
- Timber.i("Change a student result: Success")
- view?.recreateMainView()
- }
- Status.ERROR -> {
- Timber.i("Change a student result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
- }
- }.afterLoading {
- view?.popViewToMain()
- }.launch("switch")
+ resourceFlow { studentRepository.switchStudent(studentWithSemesters!!) }
+ .logResourceStatus("change student")
+ .onResourceSuccess { view?.recreateMainView() }
+ .onResourceNotLoading { view?.popViewToMain() }
+ .onResourceError(errorHandler::dispatch)
+ .launch("switch")
}
fun onRemoveSelected() {
@@ -131,7 +101,7 @@ class AccountDetailsPresenter @Inject constructor(
fun onLogoutConfirm() {
if (studentWithSemesters == null) return
- flowWithResource {
+ resourceFlow {
val studentToLogout = studentWithSemesters!!.student
studentRepository.logoutStudent(studentToLogout)
@@ -141,13 +111,13 @@ class AccountDetailsPresenter @Inject constructor(
studentRepository.switchStudent(students[0])
}
- return@flowWithResource students
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Attempt to logout user")
- Status.SUCCESS -> view?.run {
+ students
+ }
+ .logResourceStatus("logout user")
+ .onResourceSuccess {
+ view?.run {
when {
- it.data!!.isEmpty() -> {
+ it.isEmpty() -> {
Timber.i("Logout result: Open login view")
syncManager.stopSyncWorker()
openClearLoginView()
@@ -162,18 +132,16 @@ class AccountDetailsPresenter @Inject constructor(
}
}
}
- Status.ERROR -> {
- Timber.i("Logout result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .onResourceNotLoading {
+ if (studentWithSemesters?.student?.isCurrent == true) {
+ view?.popViewToMain()
+ } else {
+ view?.popViewToAccounts()
}
}
- }.afterLoading {
- if (studentWithSemesters?.student?.isCurrent == true) {
- view?.popViewToMain()
- } else {
- view?.popViewToAccounts()
- }
- }.launch("logout")
+ .onResourceError(errorHandler::dispatch)
+ .launch("logout")
}
private fun showErrorViewOnError(message: String, error: Throwable) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt
index 67ecdb5fc..c401158ea 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountedit/AccountEditPresenter.kt
@@ -1,15 +1,12 @@
package io.github.wulkanowy.ui.modules.account.accountedit
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AppInfo
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -38,43 +35,26 @@ class AccountEditPresenter @Inject constructor(
}
private fun loadData() {
- flowWithResource {
- studentRepository.getStudentById(student.id, false).avatarColor
- }.onEach { resource ->
- when (resource.status) {
- Status.LOADING -> Timber.i("Attempt to load student")
- Status.SUCCESS -> {
- view?.updateSelectedColorData(resource.data?.toInt()!!)
- Timber.i("Attempt to load student: Success")
- }
- Status.ERROR -> {
- Timber.i("Attempt to load student: An exception occurred")
- errorHandler.dispatch(resource.error!!)
- }
- }
- }.launch("load_data")
+ resourceFlow { studentRepository.getStudentById(student.id, false).avatarColor }
+ .logResourceStatus("load student")
+ .onResourceSuccess { view?.updateSelectedColorData(it.toInt()) }
+ .onResourceError(errorHandler::dispatch)
+ .launch("load_data")
}
fun changeStudentNickAndAvatar(nick: String, avatarColor: Int) {
- flowWithResource {
- val studentNick =
- StudentNickAndAvatar(nick = nick.trim(), avatarColor = avatarColor.toLong())
- .apply { id = student.id }
+ resourceFlow {
+ val studentNick = StudentNickAndAvatar(
+ nick = nick.trim(),
+ avatarColor = avatarColor.toLong()
+ ).apply { id = student.id }
+
studentRepository.updateStudentNickAndAvatar(studentNick)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Attempt to change a student nick and avatar")
- Status.SUCCESS -> {
- Timber.i("Change a student nick and avatar result: Success")
- view?.recreateMainView()
- }
- Status.ERROR -> {
- Timber.i("Change a student nick and avatar result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
- }
}
- .afterLoading { view?.popView() }
+ .logResourceStatus("change student nick and avatar")
+ .onResourceSuccess { view?.recreateMainView() }
+ .onResourceNotLoading { view?.popView() }
+ .onResourceError(errorHandler::dispatch)
.launch("update_student")
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt
index 39d8fce24..32c07f80a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountquick/AccountQuickPresenter.kt
@@ -1,14 +1,11 @@
package io.github.wulkanowy.ui.modules.account.accountquick
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.account.AccountItem
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -43,21 +40,11 @@ class AccountQuickPresenter @Inject constructor(
return
}
- flowWithResource { studentRepository.switchStudent(studentWithSemesters) }
- .onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Attempt to change a student")
- Status.SUCCESS -> {
- Timber.i("Change a student result: Success")
- view?.recreateMainView()
- }
- Status.ERROR -> {
- Timber.i("Change a student result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
- }
- }
- .afterLoading { view?.popView() }
+ resourceFlow { studentRepository.switchStudent(studentWithSemesters) }
+ .logResourceStatus("change student")
+ .onResourceSuccess { view?.recreateMainView() }
+ .onResourceNotLoading { view?.popView() }
+ .onResourceError(errorHandler::dispatch)
.launch("switch")
}
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 54d29bcf8..7fcbd002e 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
@@ -1,7 +1,7 @@
package io.github.wulkanowy.ui.modules.attendance
import android.annotation.SuppressLint
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.repositories.AttendanceRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
@@ -9,18 +9,7 @@ import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.capitalise
-import io.github.wulkanowy.utils.flowWithResource
-import io.github.wulkanowy.utils.flowWithResourceIn
-import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
-import io.github.wulkanowy.utils.isExcusableOrNotExcused
-import io.github.wulkanowy.utils.isHolidays
-import io.github.wulkanowy.utils.nextSchoolDay
-import io.github.wulkanowy.utils.previousOrSameSchoolDay
-import io.github.wulkanowy.utils.previousSchoolDay
-import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
@@ -213,93 +202,77 @@ class AttendancePresenter @Inject constructor(
var isParent = false
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
isParent = student.isParent
val semester = semesterRepository.getCurrentSemester(student)
attendanceRepository.getAttendance(
- student,
- semester,
- currentDate,
- currentDate,
- forceRefresh
+ student = student,
+ semester = semester,
+ start = currentDate,
+ end = currentDate,
+ forceRefresh = forceRefresh
)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- view?.showExcuseButton(false)
- if (!it.data.isNullOrEmpty()) {
- val filteredAttendance = if (prefRepository.isShowPresent) {
- it.data
- } else {
- it.data.filter { item -> !item.presence }
- }
-
- view?.run {
- enableSwipe(true)
- showRefresh(true)
- showProgress(false)
- showErrorView(false)
- showEmpty(filteredAttendance.isEmpty())
- showContent(filteredAttendance.isNotEmpty())
- updateData(filteredAttendance.sortedBy { item -> item.number })
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading attendance result: Success")
- val filteredAttendance = if (prefRepository.isShowPresent) {
- it.data.orEmpty()
- } else {
- it.data?.filter { item -> !item.presence }.orEmpty()
- }
-
- isVulcanExcusedFunctionEnabled =
- filteredAttendance.any { item -> item.excusable }
-
- view?.apply {
- updateData(filteredAttendance.sortedBy { item -> item.number })
- showEmpty(filteredAttendance.isEmpty())
- showErrorView(false)
- showContent(filteredAttendance.isNotEmpty())
- val anyExcusables = filteredAttendance.any { it.isExcusableOrNotExcused }
- showExcuseButton(anyExcusables && (isParent || isVulcanExcusedFunctionEnabled))
- }
- analytics.logEvent(
- "load_data",
- "type" to "attendance",
- "items" to it.data!!.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading attendance result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load attendance")
+ .onResourceLoading {
+ view?.showExcuseButton(false)
+ }
+ .mapResourceData {
+ if (prefRepository.isShowPresent) {
+ it
+ } else {
+ it.filter { item -> !item.presence }
+ }.sortedBy { item -> item.number }
+ }
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showEmpty(it.isEmpty())
+ showContent(it.isNotEmpty())
+ updateData(it)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ isVulcanExcusedFunctionEnabled = it.any { item -> item.excusable }
+ val anyExcusables = it.any { it.isExcusableOrNotExcused }
+ view?.showExcuseButton(anyExcusables && (isParent || isVulcanExcusedFunctionEnabled))
+
+ analytics.logEvent(
+ "load_data",
+ "type" to "attendance",
+ "items" to it.size
+ )
}
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ showRefresh(false)
+ showProgress(false)
+ enableSwipe(true)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun excuseAbsence(reason: String?, toExcuseList: List) {
- flowWithResource {
+ resourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
attendanceRepository.excuseForAbsence(student, semester, toExcuseList, reason)
}.onEach {
- when (it.status) {
- Status.LOADING -> view?.run {
+ when (it) {
+ is Resource.Loading -> view?.run {
Timber.i("Excusing absence started")
showProgress(true)
showContent(false)
showExcuseButton(false)
}
- Status.SUCCESS -> {
+ is Resource.Success -> {
Timber.i("Excusing for absence result: Success")
analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size)
attendanceToExcuseList.clear()
@@ -311,9 +284,9 @@ class AttendancePresenter @Inject constructor(
}
loadData(forceRefresh = true)
}
- Status.ERROR -> {
+ is Resource.Error -> {
Timber.i("Excusing for absence result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ errorHandler.dispatch(it.error)
loadData()
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt
index dd1644a98..e750b8d57 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt
@@ -74,7 +74,7 @@ class AttendanceSummaryFragment :
binding.attendanceSummarySubjectsContainer.elevation = requireContext().dpToPx(1f)
}
- override fun updateSubjects(data: ArrayList) {
+ override fun updateSubjects(data: Collection) {
with(subjectsAdapter) {
clear()
addAll(data)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt
index 8b603837b..281999176 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt
@@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.attendance.summary
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
@@ -10,9 +10,6 @@ import io.github.wulkanowy.data.repositories.SubjectRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.time.Month
import javax.inject.Inject
@@ -75,11 +72,9 @@ class AttendanceSummaryPresenter @Inject constructor(
}
private fun loadData(subjectId: Int, forceRefresh: Boolean = false) {
- Timber.i("Loading attendance summary data started")
-
currentSubjectId = subjectId
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
@@ -89,47 +84,37 @@ class AttendanceSummaryPresenter @Inject constructor(
subjectId = subjectId,
forceRefresh = forceRefresh
)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- if (!it.data.isNullOrEmpty()) {
- view?.run {
- enableSwipe(true)
- showRefresh(true)
- showProgress(false)
- showContent(true)
- showErrorView(false)
- updateDataSet(sortItems(it.data))
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading attendance summary result: Success")
- view?.apply {
- showErrorView(false)
- showEmpty(it.data!!.isEmpty())
- showContent(it.data.isNotEmpty())
- updateDataSet(sortItems(it.data))
- }
- analytics.logEvent(
- "load_data",
- "type" to "attendance_summary",
- "items" to it.data!!.size,
- "item_id" to subjectId
- )
- }
- Status.ERROR -> {
- Timber.i("Loading attendance summary result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load attendance summary")
+ .mapResourceData(this::sortItems)
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showContent(it.isNotEmpty())
+ showEmpty(it.isEmpty())
+ updateDataSet(it)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "attendance_summary",
+ "items" to it.size,
+ "item_id" to subjectId
+ )
}
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ showProgress(false)
+ showRefresh(false)
+ enableSwipe(true)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun sortItems(items: List) = items.sortedByDescending { item ->
@@ -148,27 +133,20 @@ class AttendanceSummaryPresenter @Inject constructor(
}
private fun loadSubjects() {
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
subjectRepository.getSubjects(student, semester)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading attendance summary subjects started")
- Status.SUCCESS -> {
- subjects = it.data!!
-
- Timber.i("Loading attendance summary subjects result: Success")
- view?.run {
- view?.updateSubjects(ArrayList(it.data.map { subject -> subject.name }))
- showSubjects(true)
- }
- }
- Status.ERROR -> {
- Timber.i("Loading attendance summary subjects result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load attendance summary subjects")
+ .onResourceData {
+ subjects = it
+ view?.run {
+ view?.updateSubjects(it.map { subject -> subject.name }.toList())
+ showSubjects(true)
}
}
- }.launch("subjects")
+ .onResourceError(errorHandler::dispatch)
+ .launch("subjects")
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt
index 66f370c5c..99192f185 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt
@@ -25,7 +25,7 @@ interface AttendanceSummaryView : BaseView {
fun updateDataSet(data: List)
- fun updateSubjects(data: ArrayList)
+ fun updateSubjects(data: Collection)
fun showSubjects(show: Boolean)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt
index dab170daa..f53648930 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt
@@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.conference
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.data.repositories.ConferenceRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
@@ -8,9 +8,6 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -64,50 +61,39 @@ class ConferencePresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
- Timber.i("Loading conference data started")
-
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
conferenceRepository.getConferences(student, semester, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- if (!it.data.isNullOrEmpty()) {
- view?.run {
- enableSwipe(true)
- showRefresh(true)
- showProgress(false)
- showContent(true)
- updateData(it.data.sortedByDescending { conference -> conference.date })
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading conference result: Success")
- view?.run {
- updateData(it.data!!.sortedByDescending { conference -> conference.date })
- showContent(it.data.isNotEmpty())
- showEmpty(it.data.isEmpty())
- showErrorView(false)
- }
- analytics.logEvent(
- "load_data",
- "type" to "conferences",
- "items" to it.data!!.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading conference result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load conference data")
+ .mapResourceData { it.sortedByDescending { conference -> conference.date } }
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showContent(it.isNotEmpty())
+ showEmpty(it.isEmpty())
+ updateData(it)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "conferences",
+ "items" to it.size
+ )
}
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showRefresh(false)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
index 88c281ecd..65832bdb1 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
@@ -204,4 +204,4 @@ class DashboardFragment : BaseFragment(R.layout.fragme
presenter.onDetachView()
super.onDestroyView()
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index cad8d112b..c33955bc7 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -1,7 +1,6 @@
package io.github.wulkanowy.ui.modules.dashboard
-import io.github.wulkanowy.data.Resource
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student
@@ -10,7 +9,6 @@ import io.github.wulkanowy.data.repositories.*
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.calculatePercentage
-import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
@@ -104,7 +102,7 @@ class DashboardPresenter @Inject constructor(
forceRefresh: Boolean
) = dashboardTilesToLoad.filter { newItemToLoad ->
dashboardLoadedTiles.none { it == newItemToLoad } || forceRefresh
- || newItemToLoad == DashboardItem.Tile.ADMIN_MESSAGE
+ || newItemToLoad == DashboardItem.Tile.ADMIN_MESSAGE
}
private fun removeUnselectedTiles(tilesToLoad: List) {
@@ -225,27 +223,26 @@ class DashboardPresenter @Inject constructor(
val semester = semesterRepository.getCurrentSemester(student)
val selectedTiles = preferencesRepository.selectedDashboardTiles
+ val flowSuccess = flowOf(Resource.Success(null))
val luckyNumberFlow = luckyNumberRepository.getLuckyNumber(student, forceRefresh)
- .map {
- if (it.data == null) {
- it.copy(data = LuckyNumber(0, LocalDate.now(), 0))
- } else it
+ .mapResourceData {
+ it ?: LuckyNumber(0, LocalDate.now(), 0)
}
- .takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowOf(null)
+ .takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowSuccess
val messageFLow = messageRepository.getMessages(
student = student,
semester = semester,
folder = MessageFolder.RECEIVED,
forceRefresh = forceRefresh
- ).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowOf(null)
+ ).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
val attendanceFlow = attendanceSummaryRepository.getAttendanceSummary(
student = student,
semester = semester,
subjectId = -1,
forceRefresh = forceRefresh
- ).takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowOf(null)
+ ).takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess
emitAll(
combine(
@@ -253,17 +250,13 @@ class DashboardPresenter @Inject constructor(
messageFLow,
attendanceFlow
) { luckyNumberResource, messageResource, attendanceResource ->
- val error =
- luckyNumberResource?.error ?: messageResource?.error
- ?: attendanceResource?.error
- error?.let { throw it }
+ val resList = listOf(luckyNumberResource, messageResource, attendanceResource)
+ resList.firstNotNullOfOrNull { it.errorOrNull }?.let { throw it }
+ val isLoading = resList.any { it is Resource.Loading }
- val luckyNumber = luckyNumberResource?.data?.luckyNumber
- val messageCount = messageResource?.data?.count { it.unread }
- val attendancePercentage = attendanceResource?.data?.calculatePercentage()
-
- val isLoading =
- luckyNumberResource?.status == Status.LOADING || messageResource?.status == Status.LOADING || attendanceResource?.status == Status.LOADING
+ val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber
+ val messageCount = messageResource.dataOrNull?.count { it.unread }
+ val attendancePercentage = attendanceResource.dataOrNull?.calculatePercentage()
DashboardItem.HorizontalGroup(
isLoading = isLoading,
@@ -300,68 +293,65 @@ class DashboardPresenter @Inject constructor(
}
private fun loadGrades(student: Student, forceRefresh: Boolean) {
- flowWithResourceIn {
+ flatResourceFlow {
val semester = semesterRepository.getCurrentSemester(student)
gradeRepository.getGrades(student, semester, forceRefresh)
- }.map { originalResource ->
- val filteredSubjectWithGrades = originalResource.data?.first
- .orEmpty()
- .filter { it.date >= LocalDate.now().minusDays(7) }
- .groupBy { it.subject }
- .mapValues { entry ->
- entry.value
- .take(5)
- .sortedByDescending { it.date }
- }
- .toList()
- .sortedByDescending { (_, grades) -> grades[0].date }
- .toMap()
+ }
+ .mapResourceData { (details, _) ->
+ val filteredSubjectWithGrades = details
+ .filter { it.date >= LocalDate.now().minusDays(7) }
+ .groupBy { it.subject }
+ .mapValues { entry ->
+ entry.value
+ .take(5)
+ .sortedByDescending { it.date }
+ }
+ .toList()
+ .sortedByDescending { (_, grades) -> grades[0].date }
+ .toMap()
- Resource(
- status = originalResource.status,
- data = filteredSubjectWithGrades.takeIf { originalResource.data != null },
- error = originalResource.error
- )
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- Timber.i("Loading dashboard grades data started")
- if (forceRefresh) return@onEach
+ filteredSubjectWithGrades
+ }
+ .onEach {
+ when (it) {
+ is Resource.Loading -> {
+ Timber.i("Loading dashboard grades data started")
+ if (forceRefresh) return@onEach
+ updateData(
+ DashboardItem.Grades(
+ subjectWithGrades = it.dataOrNull,
+ gradeTheme = preferencesRepository.gradeColorTheme,
+ isLoading = true
+ ), forceRefresh
+ )
- updateData(
- DashboardItem.Grades(
- subjectWithGrades = it.data,
- gradeTheme = preferencesRepository.gradeColorTheme,
- isLoading = true
- ), forceRefresh
- )
-
- if (!it.data.isNullOrEmpty()) {
- firstLoadedItemList += DashboardItem.Type.GRADES
+ if (!it.dataOrNull.isNullOrEmpty()) {
+ firstLoadedItemList += DashboardItem.Type.GRADES
+ }
+ }
+ is Resource.Success -> {
+ Timber.i("Loading dashboard grades result: Success")
+ updateData(
+ DashboardItem.Grades(
+ subjectWithGrades = it.data,
+ gradeTheme = preferencesRepository.gradeColorTheme
+ ),
+ forceRefresh
+ )
+ }
+ is Resource.Error -> {
+ Timber.i("Loading dashboard grades result: An exception occurred")
+ errorHandler.dispatch(it.error)
+ updateData(DashboardItem.Grades(error = it.error), forceRefresh)
}
}
- Status.SUCCESS -> {
- Timber.i("Loading dashboard grades result: Success")
- updateData(
- DashboardItem.Grades(
- subjectWithGrades = it.data,
- gradeTheme = preferencesRepository.gradeColorTheme
- ),
- forceRefresh
- )
- }
- Status.ERROR -> {
- Timber.i("Loading dashboard grades result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- updateData(DashboardItem.Grades(error = it.error), forceRefresh)
- }
}
- }.launchWithUniqueRefreshJob("dashboard_grades", forceRefresh)
+ .launchWithUniqueRefreshJob("dashboard_grades", forceRefresh)
}
private fun loadLessons(student: Student, forceRefresh: Boolean) {
- flowWithResourceIn {
+ flatResourceFlow {
val semester = semesterRepository.getCurrentSemester(student)
val date = LocalDate.now().nextOrSameSchoolDay
@@ -372,40 +362,41 @@ class DashboardPresenter @Inject constructor(
end = date.plusDays(1),
forceRefresh = forceRefresh
)
+ }
+ .onEach {
+ when (it) {
+ is Resource.Loading -> {
+ Timber.i("Loading dashboard lessons data started")
+ if (forceRefresh) return@onEach
+ updateData(
+ DashboardItem.Lessons(it.dataOrNull, isLoading = true),
+ forceRefresh
+ )
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- Timber.i("Loading dashboard lessons data started")
- if (forceRefresh) return@onEach
- updateData(
- DashboardItem.Lessons(it.data, isLoading = true),
- forceRefresh
- )
-
- if (!it.data?.lessons.isNullOrEmpty()) {
- firstLoadedItemList += DashboardItem.Type.LESSONS
+ if (!it.dataOrNull?.lessons.isNullOrEmpty()) {
+ firstLoadedItemList += DashboardItem.Type.LESSONS
+ }
+ }
+ is Resource.Success -> {
+ Timber.i("Loading dashboard lessons result: Success")
+ updateData(
+ DashboardItem.Lessons(it.data), forceRefresh
+ )
+ }
+ is Resource.Error -> {
+ Timber.i("Loading dashboard lessons result: An exception occurred")
+ errorHandler.dispatch(it.error)
+ updateData(
+ DashboardItem.Lessons(error = it.error), forceRefresh
+ )
}
}
- Status.SUCCESS -> {
- Timber.i("Loading dashboard lessons result: Success")
- updateData(
- DashboardItem.Lessons(it.data), forceRefresh
- )
- }
- Status.ERROR -> {
- Timber.i("Loading dashboard lessons result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- updateData(
- DashboardItem.Lessons(error = it.error), forceRefresh
- )
- }
}
- }.launchWithUniqueRefreshJob("dashboard_lessons", forceRefresh)
+ .launchWithUniqueRefreshJob("dashboard_lessons", forceRefresh)
}
private fun loadHomework(student: Student, forceRefresh: Boolean) {
- flowWithResourceIn {
+ flatResourceFlow {
val semester = semesterRepository.getCurrentSemester(student)
val date = LocalDate.now().nextOrSameSchoolDay
@@ -416,73 +407,79 @@ class DashboardPresenter @Inject constructor(
end = date,
forceRefresh = forceRefresh
)
- }.map { homeworkResource ->
- val currentDate = LocalDate.now()
+ }
+ .mapResourceData { homework ->
+ val currentDate = LocalDate.now()
- val filteredHomework = homeworkResource.data
- ?.filter { (it.date.isAfter(currentDate) || it.date == currentDate) && !it.isDone }
- ?.sortedBy { it.date }
+ val filteredHomework = homework.filter {
+ (it.date.isAfter(currentDate) || it.date == currentDate) && !it.isDone
+ }.sortedBy { it.date }
- homeworkResource.copy(data = filteredHomework)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- Timber.i("Loading dashboard homework data started")
- if (forceRefresh) return@onEach
- updateData(
- DashboardItem.Homework(it.data ?: emptyList(), isLoading = true),
- forceRefresh
- )
+ filteredHomework
+ }
+ .onEach {
+ when (it) {
+ is Resource.Loading -> {
+ Timber.i("Loading dashboard homework data started")
+ if (forceRefresh) return@onEach
+ val data = it.dataOrNull.orEmpty()
+ updateData(
+ DashboardItem.Homework(data, isLoading = true),
+ forceRefresh
+ )
- if (!it.data.isNullOrEmpty()) {
- firstLoadedItemList += DashboardItem.Type.HOMEWORK
+ if (data.isNotEmpty()) {
+ firstLoadedItemList += DashboardItem.Type.HOMEWORK
+ }
+ }
+ is Resource.Success -> {
+ Timber.i("Loading dashboard homework result: Success")
+ updateData(DashboardItem.Homework(it.data), forceRefresh)
+ }
+ is Resource.Error -> {
+ Timber.i("Loading dashboard homework result: An exception occurred")
+ errorHandler.dispatch(it.error)
+ updateData(DashboardItem.Homework(error = it.error), forceRefresh)
}
}
- Status.SUCCESS -> {
- Timber.i("Loading dashboard homework result: Success")
- updateData(DashboardItem.Homework(it.data ?: emptyList()), forceRefresh)
- }
- Status.ERROR -> {
- Timber.i("Loading dashboard homework result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- updateData(DashboardItem.Homework(error = it.error), forceRefresh)
- }
}
- }.launchWithUniqueRefreshJob("dashboard_homework", forceRefresh)
+ .launchWithUniqueRefreshJob("dashboard_homework", forceRefresh)
}
private fun loadSchoolAnnouncements(student: Student, forceRefresh: Boolean) {
- flowWithResourceIn {
+ flatResourceFlow {
schoolAnnouncementRepository.getSchoolAnnouncements(student, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- Timber.i("Loading dashboard announcements data started")
- if (forceRefresh) return@onEach
- updateData(
- DashboardItem.Announcements(it.data ?: emptyList(), isLoading = true),
- forceRefresh
- )
+ }
+ .onEach {
+ when (it) {
+ is Resource.Loading -> {
+ Timber.i("Loading dashboard announcements data started")
+ if (forceRefresh) return@onEach
+ updateData(
+ DashboardItem.Announcements(it.dataOrNull.orEmpty(), isLoading = true),
+ forceRefresh
+ )
- if (!it.data.isNullOrEmpty()) {
- firstLoadedItemList += DashboardItem.Type.ANNOUNCEMENTS
+ if (!it.dataOrNull.isNullOrEmpty()) {
+ firstLoadedItemList += DashboardItem.Type.ANNOUNCEMENTS
+ }
+ }
+ is Resource.Success -> {
+ Timber.i("Loading dashboard announcements result: Success")
+ updateData(DashboardItem.Announcements(it.data), forceRefresh)
+ }
+ is Resource.Error -> {
+ Timber.i("Loading dashboard announcements result: An exception occurred")
+ errorHandler.dispatch(it.error)
+ updateData(DashboardItem.Announcements(error = it.error), forceRefresh)
}
}
- Status.SUCCESS -> {
- Timber.i("Loading dashboard announcements result: Success")
- updateData(DashboardItem.Announcements(it.data ?: emptyList()), forceRefresh)
- }
- Status.ERROR -> {
- Timber.i("Loading dashboard announcements result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- updateData(DashboardItem.Announcements(error = it.error), forceRefresh)
- }
}
- }.launchWithUniqueRefreshJob("dashboard_announcements", forceRefresh)
+ .launchWithUniqueRefreshJob("dashboard_announcements", forceRefresh)
}
private fun loadExams(student: Student, forceRefresh: Boolean) {
- flowWithResourceIn {
+ flatResourceFlow {
val semester = semesterRepository.getCurrentSemester(student)
examRepository.getExams(
@@ -493,40 +490,37 @@ class DashboardPresenter @Inject constructor(
forceRefresh = forceRefresh
)
}
- .map { examResource ->
- val sortedExams = examResource.data?.sortedBy { it.date }
-
- examResource.copy(data = sortedExams)
- }
+ .mapResourceData { exams -> exams.sortedBy { exam -> exam.date } }
.onEach {
- when (it.status) {
- Status.LOADING -> {
+ when (it) {
+ is Resource.Loading -> {
Timber.i("Loading dashboard exams data started")
if (forceRefresh) return@onEach
updateData(
- DashboardItem.Exams(it.data.orEmpty(), isLoading = true),
+ DashboardItem.Exams(it.dataOrNull.orEmpty(), isLoading = true),
forceRefresh
)
- if (!it.data.isNullOrEmpty()) {
+ if (!it.dataOrNull.isNullOrEmpty()) {
firstLoadedItemList += DashboardItem.Type.EXAMS
}
}
- Status.SUCCESS -> {
+ is Resource.Success -> {
Timber.i("Loading dashboard exams result: Success")
- updateData(DashboardItem.Exams(it.data ?: emptyList()), forceRefresh)
+ updateData(DashboardItem.Exams(it.data), forceRefresh)
}
- Status.ERROR -> {
+ is Resource.Error -> {
Timber.i("Loading dashboard exams result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ errorHandler.dispatch(it.error)
updateData(DashboardItem.Exams(error = it.error), forceRefresh)
}
}
- }.launchWithUniqueRefreshJob("dashboard_exams", forceRefresh)
+ }
+ .launchWithUniqueRefreshJob("dashboard_exams", forceRefresh)
}
private fun loadConferences(student: Student, forceRefresh: Boolean) {
- flowWithResourceIn {
+ flatResourceFlow {
val semester = semesterRepository.getCurrentSemester(student)
conferenceRepository.getConferences(
@@ -535,59 +529,62 @@ class DashboardPresenter @Inject constructor(
forceRefresh = forceRefresh,
startDate = Instant.now(),
)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- Timber.i("Loading dashboard conferences data started")
- if (forceRefresh) return@onEach
- updateData(
- DashboardItem.Conferences(it.data ?: emptyList(), isLoading = true),
- forceRefresh
- )
+ }
+ .onEach {
+ when (it) {
+ is Resource.Loading -> {
+ Timber.i("Loading dashboard conferences data started")
+ if (forceRefresh) return@onEach
+ updateData(
+ DashboardItem.Conferences(it.dataOrNull.orEmpty(), isLoading = true),
+ forceRefresh
+ )
- if (!it.data.isNullOrEmpty()) {
- firstLoadedItemList += DashboardItem.Type.CONFERENCES
+ if (!it.dataOrNull.isNullOrEmpty()) {
+ firstLoadedItemList += DashboardItem.Type.CONFERENCES
+ }
+ }
+ is Resource.Success -> {
+ Timber.i("Loading dashboard conferences result: Success")
+ updateData(DashboardItem.Conferences(it.data), forceRefresh)
+ }
+ is Resource.Error -> {
+ Timber.i("Loading dashboard conferences result: An exception occurred")
+ errorHandler.dispatch(it.error)
+ updateData(DashboardItem.Conferences(error = it.error), forceRefresh)
}
}
- Status.SUCCESS -> {
- Timber.i("Loading dashboard conferences result: Success")
- updateData(DashboardItem.Conferences(it.data ?: emptyList()), forceRefresh)
- }
- Status.ERROR -> {
- Timber.i("Loading dashboard conferences result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- updateData(DashboardItem.Conferences(error = it.error), forceRefresh)
- }
}
- }.launchWithUniqueRefreshJob("dashboard_conferences", forceRefresh)
+ .launchWithUniqueRefreshJob("dashboard_conferences", forceRefresh)
}
private fun loadAdminMessage(student: Student, forceRefresh: Boolean) {
- flowWithResourceIn { adminMessageRepository.getAdminMessages(student) }
- .map {
- val isDismissed = it.data?.id in preferencesRepository.dismissedAdminMessageIds
- it.copy(data = it.data.takeUnless { isDismissed })
+ flatResourceFlow { adminMessageRepository.getAdminMessages(student) }
+ .filter {
+ val data = it.dataOrNull ?: return@filter true
+ val isDismissed = data.id in preferencesRepository.dismissedAdminMessageIds
+ !isDismissed
}
.onEach {
- when (it.status) {
- Status.LOADING -> {
+ when (it) {
+ is Resource.Loading -> {
Timber.i("Loading dashboard admin message data started")
if (forceRefresh) return@onEach
updateData(DashboardItem.AdminMessages(), forceRefresh)
}
- Status.SUCCESS -> {
+ is Resource.Success -> {
Timber.i("Loading dashboard admin message result: Success")
updateData(
dashboardItem = DashboardItem.AdminMessages(adminMessage = it.data),
forceRefresh = forceRefresh
)
}
- Status.ERROR -> {
+ is Resource.Error -> {
Timber.i("Loading dashboard admin message result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ errorHandler.dispatch(it.error)
updateData(
dashboardItem = DashboardItem.AdminMessages(
- adminMessage = it.data,
+ adminMessage = null,
error = it.error
),
forceRefresh = forceRefresh
@@ -740,7 +737,7 @@ class DashboardPresenter @Inject constructor(
if (forceRefresh) {
onEach {
- if (it.status == Status.SUCCESS) {
+ if (it is Resource.Success) {
cancelJobs(jobName)
}
}.launch(jobName)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerPresenter.kt
index 4310ff87c..7adb56b8c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerPresenter.kt
@@ -1,11 +1,11 @@
package io.github.wulkanowy.ui.modules.debug.logviewer
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.repositories.LoggerRepository
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -23,19 +23,21 @@ class LogViewerPresenter @Inject constructor(
}
fun onShareLogsSelected(): Boolean {
- flowWithResource { loggerRepository.getLogFiles() }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.d("Loading logs files started")
- Status.SUCCESS -> {
- Timber.i("Loading logs files result: ${it.data!!.joinToString { file -> file.name }}")
- view?.shareLogs(it.data)
- }
- Status.ERROR -> {
- Timber.i("Loading logs files result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ resourceFlow { loggerRepository.getLogFiles() }
+ .onEach {
+ when (it) {
+ is Resource.Loading -> Timber.d("Loading logs files started")
+ is Resource.Success -> {
+ Timber.i("Loading logs files result: ${it.data.joinToString { file -> file.name }}")
+ view?.shareLogs(it.data)
+ }
+ is Resource.Error -> {
+ Timber.i("Loading logs files result: An exception occurred")
+ errorHandler.dispatch(it.error)
+ }
}
}
- }.launch("share")
+ .launch("share")
return true
}
@@ -44,18 +46,20 @@ class LogViewerPresenter @Inject constructor(
}
private fun loadLogFile() {
- flowWithResource { loggerRepository.getLastLogLines() }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.d("Loading last log file started")
- Status.SUCCESS -> {
- Timber.i("Loading last log file result: load ${it.data!!.size} lines")
- view?.setLines(it.data)
- }
- Status.ERROR -> {
- Timber.i("Loading last log file result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ resourceFlow { loggerRepository.getLastLogLines() }
+ .onEach {
+ when (it) {
+ is Resource.Loading -> Timber.d("Loading last log file started")
+ is Resource.Success -> {
+ Timber.i("Loading last log file result: load ${it.data.size} lines")
+ view?.setLines(it.data)
+ }
+ is Resource.Error -> {
+ Timber.i("Loading last log file result: An exception occurred")
+ errorHandler.dispatch(it.error)
+ }
}
}
- }.launch("file")
+ .launch("file")
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt
index 582641fcf..99b0bcb87 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt
@@ -1,21 +1,13 @@
package io.github.wulkanowy.ui.modules.exam
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.repositories.ExamRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
-import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
-import io.github.wulkanowy.utils.isHolidays
-import io.github.wulkanowy.utils.monday
-import io.github.wulkanowy.utils.nextOrSameSchoolDay
-import io.github.wulkanowy.utils.sunday
-import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
@@ -86,61 +78,57 @@ class ExamPresenter @Inject constructor(
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
- }.catch {
- Timber.i("Loading semester result: An exception occurred")
- }.onEach {
- baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
- currentDate = baseDate
- reloadNavigation()
- }.launch("holidays")
+ }
+ .catch { Timber.i("Loading semester result: An exception occurred") }
+ .onEach {
+ baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
+ currentDate = baseDate
+ reloadNavigation()
+ }
+ .launch("holidays")
}
private fun loadData(forceRefresh: Boolean = false) {
- Timber.i("Loading exam data started")
-
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
- examRepository.getExams(student, semester, currentDate.monday, currentDate.sunday, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- if (!it.data.isNullOrEmpty()) {
- view?.run {
- enableSwipe(true)
- showRefresh(true)
- showProgress(false)
- showContent(true)
- updateData(createExamItems(it.data))
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading exam result: Success")
- view?.apply {
- updateData(createExamItems(it.data!!))
- showEmpty(it.data.isEmpty())
- showErrorView(false)
- showContent(it.data.isNotEmpty())
- }
- analytics.logEvent(
- "load_data",
- "type" to "exam",
- "items" to it.data!!.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading exam result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ examRepository.getExams(
+ student = student,
+ semester = semester,
+ start = currentDate.monday,
+ end = currentDate.sunday,
+ forceRefresh = forceRefresh
+ )
+ }
+ .logResourceStatus("load exam data")
+ .mapResourceData { createExamItems(it) }
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showContent(it.isNotEmpty())
+ showEmpty(it.isEmpty())
+ updateData(it)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "exam",
+ "items" to it.size
+ )
}
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showRefresh(false)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@@ -181,8 +169,10 @@ class ExamPresenter @Inject constructor(
view?.apply {
showPreButton(!currentDate.minusDays(7).isHolidays)
showNextButton(!currentDate.plusDays(7).isHolidays)
- updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " +
- currentDate.sunday.toFormattedString("dd.MM"))
+ updateNavigationWeek(
+ "${currentDate.monday.toFormattedString("dd.MM")} - " +
+ currentDate.sunday.toFormattedString("dd.MM")
+ )
}
}
}
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 0c18ce95e..b6733d4f2 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
@@ -1,7 +1,6 @@
package io.github.wulkanowy.ui.modules.grade
-import io.github.wulkanowy.data.Resource
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
@@ -13,12 +12,10 @@ import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.*
import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.changeModifier
-import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.map
import javax.inject.Inject
@OptIn(FlowPreview::class)
@@ -35,7 +32,7 @@ class GradeAverageProvider @Inject constructor(
private val isOptionalArithmeticAverage get() = preferencesRepository.isOptionalArithmeticAverage
fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) =
- flowWithResourceIn {
+ flatResourceFlow {
val semesters = semesterRepository.getSemesters(student)
when (preferencesRepository.gradeAverageMode) {
@@ -81,17 +78,17 @@ class GradeAverageProvider @Inject constructor(
val firstSemesterGradeSubjects = getGradeSubjects(student, firstSemester, forceRefresh)
return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject ->
- if (firstSemesterGradeSubject.status == Status.ERROR) {
+ if (firstSemesterGradeSubject.errorOrNull != null) {
return@combine firstSemesterGradeSubject
}
val isAnyVulcanAverageInFirstSemester =
- firstSemesterGradeSubject.data.orEmpty().any { it.isVulcanAverage }
+ firstSemesterGradeSubject.dataOrNull.orEmpty().any { it.isVulcanAverage }
val isAnyVulcanAverageInSecondSemester =
- secondSemesterGradeSubject.data.orEmpty().any { it.isVulcanAverage }
+ secondSemesterGradeSubject.dataOrNull.orEmpty().any { it.isVulcanAverage }
- val updatedData = secondSemesterGradeSubject.data?.map { secondSemesterSubject ->
- val firstSemesterSubject = firstSemesterGradeSubject.data.orEmpty()
+ val updatedData = secondSemesterGradeSubject.dataOrNull?.map { secondSemesterSubject ->
+ val firstSemesterSubject = firstSemesterGradeSubject.dataOrNull.orEmpty()
.singleOrNull { it.subject == secondSemesterSubject.subject }
val updatedAverage = if (averageMode == ALL_YEAR) {
@@ -113,7 +110,7 @@ class GradeAverageProvider @Inject constructor(
}
secondSemesterSubject.copy(average = updatedAverage)
}
- secondSemesterGradeSubject.copy(data = updatedData)
+ secondSemesterGradeSubject.mapData { updatedData!! }
}
}
@@ -166,17 +163,17 @@ class GradeAverageProvider @Inject constructor(
val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh)
- .map { res ->
- val (details, summaries) = res.data ?: null to null
- val isAnyAverage = summaries.orEmpty().any { it.average != .0 }
- val allGrades = details.orEmpty().groupBy { it.subject }
+ .mapResourceData { res ->
+ val (details, summaries) = res
+ val isAnyAverage = summaries.any { it.average != .0 }
+ val allGrades = details.groupBy { it.subject }
- val items = summaries?.emulateEmptySummaries(
+ val items = summaries.emulateEmptySummaries(
student = student,
semester = semester,
grades = allGrades.toList(),
calcAverage = isAnyAverage
- )?.map { summary ->
+ ).map { summary ->
val grades = allGrades[summary.subject].orEmpty()
GradeSubject(
subject = summary.subject,
@@ -190,7 +187,7 @@ class GradeAverageProvider @Inject constructor(
)
}
- Resource(res.status, items, res.error)
+ items
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt
index 76e88bcdb..0ae6521cf 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradePresenter.kt
@@ -1,16 +1,16 @@
package io.github.wulkanowy.ui.modules.grade
-import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.logResourceStatus
+import io.github.wulkanowy.data.onResourceData
+import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.getCurrentOrLast
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -99,32 +99,26 @@ class GradePresenter @Inject constructor(
}
private fun loadData() {
- flowWithResource {
+ resourceFlow {
val student = studentRepository.getCurrentStudent()
semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading grade data started")
- Status.SUCCESS -> {
- val current = it.data!!.getCurrentOrLast()
- selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
- schoolYear = current.schoolYear
- semesters = it.data.filter { semester -> semester.diaryId == current.diaryId }
- view?.setCurrentSemesterName(current.semesterName, schoolYear)
-
- view?.run {
- Timber.i("Loading grade result: Attempt load index $currentPageIndex")
- loadChild(currentPageIndex)
- showErrorView(false)
- showSemesterSwitch(true)
- }
- }
- Status.ERROR -> {
- Timber.i("Loading grade result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load grade data")
+ .onResourceData {
+ val current = it.getCurrentOrLast()
+ selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
+ schoolYear = current.schoolYear
+ semesters = it.filter { semester -> semester.diaryId == current.diaryId }
+ view?.setCurrentSemesterName(current.semesterName, schoolYear)
+ view?.run {
+ Timber.i("Loading grade data: Attempt load index $currentPageIndex")
+ loadChild(currentPageIndex)
+ showErrorView(false)
+ showSemesterSwitch(true)
}
}
- }.launch()
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
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 3c747b949..34594111f 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
@@ -12,6 +12,7 @@ import io.github.wulkanowy.data.enums.GradeColorTheme
import io.github.wulkanowy.databinding.DialogGradeBinding
import io.github.wulkanowy.utils.*
+
class GradeDetailsDialog : DialogFragment() {
private var binding: DialogGradeBinding by lifecycleAwareVariable()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
index 9141e0438..746601a68 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
@@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.grade.details
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.enums.GradeExpandMode
import io.github.wulkanowy.data.enums.GradeSortingMode.ALPHABETIC
@@ -14,12 +14,8 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.ui.modules.grade.GradeSubject
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
-import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -71,7 +67,7 @@ class GradeDetailsPresenter @Inject constructor(
}
fun onMarkAsReadSelected(): Boolean {
- flowWithResource {
+ resourceFlow {
val student = studentRepository.getCurrentStudent()
val semesters = semesterRepository.getSemesters(student)
val semester = semesters.first { item -> item.semesterId == currentSemesterId }
@@ -79,19 +75,11 @@ class GradeDetailsPresenter @Inject constructor(
Timber.i("Mark as read ${unreadGrades.size} grades")
gradeRepository.updateGrades(unreadGrades.map { it.apply { isRead = true } })
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Select mark grades as read")
- Status.SUCCESS -> {
- Timber.i("Mark as read result: Success")
- loadData(currentSemesterId, false)
- }
- Status.ERROR -> {
- Timber.i("Mark as read result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
- }
- }.launch("mark")
+ }
+ .logResourceStatus("mark grades as read")
+ .onResourceSuccess { loadData(currentSemesterId, false) }
+ .onResourceError(errorHandler::dispatch)
+ .launch("mark")
return true
}
@@ -138,71 +126,49 @@ class GradeDetailsPresenter @Inject constructor(
}
private fun loadData(semesterId: Int, forceRefresh: Boolean) {
- Timber.i("Loading grade details data started")
-
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh)
- }.onEach {
- Timber.d("Loading grade details status: ${it.status}, data: ${it.data != null}")
- when (it.status) {
- Status.LOADING -> {
- val items = createGradeItems(it.data.orEmpty())
- if (items.isNotEmpty()) {
- Timber.i("Loading grade details result: load cached data")
- view?.run {
- updateNewGradesAmount(it.data.orEmpty())
- enableSwipe(true)
- showRefresh(true)
- showProgress(false)
- showEmpty(false)
- showContent(true)
- updateData(
- data = items,
- expandMode = preferencesRepository.gradeExpandMode,
- gradeColorTheme = preferencesRepository.gradeColorTheme
- )
- notifyParentDataLoaded(semesterId)
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading grade details result: Success")
- updateNewGradesAmount(it.data!!)
+ }
+ .logResourceStatus("load grade details")
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showContent(it.isNotEmpty())
+ showEmpty(it.isEmpty())
+ updateNewGradesAmount(it)
updateMarkAsDoneButton()
- val items = createGradeItems(it.data)
- view?.run {
- showEmpty(items.isEmpty())
- showErrorView(false)
- showContent(items.isNotEmpty())
- updateData(
- data = items,
- expandMode = preferencesRepository.gradeExpandMode,
- gradeColorTheme = preferencesRepository.gradeColorTheme
- )
- }
- analytics.logEvent(
- "load_data",
- "type" to "grade_details",
- "items" to it.data.size
+ updateData(
+ data = createGradeItems(it),
+ expandMode = preferencesRepository.gradeExpandMode,
+ preferencesRepository.gradeColorTheme
)
}
- Status.ERROR -> {
- Timber.i("Loading grade details result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "grade_details",
+ "items" to it.size
+ )
+ }
+ .onResourceNotLoading {
+ view?.run {
+ enableSwipe(true)
+ showRefresh(false)
+ showProgress(false)
+ notifyParentDataLoaded(semesterId)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
- notifyParentDataLoaded(semesterId)
+ .catch {
+ errorHandler.dispatch(it)
+ view?.notifyParentDataLoaded(semesterId)
}
- }.catch {
- errorHandler.dispatch(it)
- view?.notifyParentDataLoaded(semesterId)
- }.launch()
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun updateNewGradesAmount(grades: List) {
@@ -267,15 +233,9 @@ class GradeDetailsPresenter @Inject constructor(
}
private fun updateGrade(grade: Grade) {
- flowWithResource { gradeRepository.updateGrade(grade) }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Attempt to update grade ${grade.id}")
- Status.SUCCESS -> Timber.i("Update grade result: Success")
- Status.ERROR -> {
- Timber.i("Update grade result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
- }
- }.launch("update")
+ resourceFlow { gradeRepository.updateGrade(grade) }
+ .logResourceStatus("update grade result ${grade.id}")
+ .onResourceError(errorHandler::dispatch)
+ .launch("update")
}
}
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 e536f473a..aa0e5999e 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
@@ -1,19 +1,12 @@
package io.github.wulkanowy.ui.modules.grade.statistics
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
-import io.github.wulkanowy.data.repositories.GradeStatisticsRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
-import io.github.wulkanowy.data.repositories.SemesterRepository
-import io.github.wulkanowy.data.repositories.StudentRepository
-import io.github.wulkanowy.data.repositories.SubjectRepository
+import io.github.wulkanowy.data.repositories.*
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -125,33 +118,26 @@ class GradeStatisticsPresenter @Inject constructor(
}
private fun loadSubjects() {
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
subjectRepository.getSubjects(student, semester)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading grade stats subjects started")
- Status.SUCCESS -> {
- subjects = requireNotNull(it.data)
- Timber.i("Loading grade stats subjects result: Success")
-
- view?.run {
- showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
- updateSubjects(
- data = it.data.map { subject -> subject.name },
- selectedIndex = it.data.indexOfFirst { subject ->
- subject.name == currentSubjectName
- },
- )
- }
- }
- Status.ERROR -> {
- Timber.i("Loading grade stats subjects result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load grade stats subjects")
+ .onResourceData {
+ subjects = it
+ view?.run {
+ showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
+ updateSubjects(
+ data = it.map { subject -> subject.name },
+ selectedIndex = it.indexOfFirst { subject ->
+ subject.name == currentSubjectName
+ },
+ )
}
}
- }.launch("subjects")
+ .onResourceError(errorHandler::dispatch)
+ .launch("subjects")
}
private fun loadDataByType(
@@ -168,7 +154,7 @@ class GradeStatisticsPresenter @Inject constructor(
else -> subjectName
}
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semesters = semesterRepository.getSemesters(student)
val semester = semesters.first { item -> item.semesterId == semesterId }
@@ -201,58 +187,43 @@ class GradeStatisticsPresenter @Inject constructor(
}
}
}
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- val isNoContent = it.data == null || checkIsNoContent(it.data, type)
- if (!isNoContent) {
- view?.run {
- showEmpty(isNoContent)
- showErrorView(false)
- enableSwipe(true)
- showRefresh(true)
- showProgress(false)
- updateData(
- newItems = if (isNoContent) emptyList() else it.data!!,
- newTheme = preferencesRepository.gradeColorTheme,
- showAllSubjectsOnStatisticsList = preferencesRepository.showAllSubjectsOnStatisticsList,
- )
- showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading grade stats result: Success")
- view?.run {
- val isNoContent = checkIsNoContent(it.data!!, type)
- showEmpty(isNoContent)
- showErrorView(false)
- updateData(
- newItems = if (isNoContent) emptyList() else it.data,
- newTheme = preferencesRepository.gradeColorTheme,
- showAllSubjectsOnStatisticsList = preferencesRepository.showAllSubjectsOnStatisticsList,
- )
- showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
- }
- analytics.logEvent(
- "load_data",
- "type" to "grade_statistics",
- "items" to it.data!!.size
+ }
+ .logResourceStatus("load grade stats data")
+ .mapResourceData {
+ val isNoContent = checkIsNoContent(it, type)
+ if (isNoContent) emptyList() else it
+ }
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showEmpty(it.isEmpty())
+ updateData(
+ newItems = it,
+ newTheme = preferencesRepository.gradeColorTheme,
+ showAllSubjectsOnStatisticsList = preferencesRepository.showAllSubjectsOnStatisticsList
)
}
- Status.ERROR -> {
- Timber.i("Loading grade stats result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "grade_statistics",
+ "items" to it.size
+ )
+ }
+ .onResourceNotLoading {
+ view?.run {
+ enableSwipe(true)
+ showRefresh(false)
+ showProgress(false)
+ notifyParentDataLoaded(semesterId)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
- notifyParentDataLoaded(semesterId)
- }
- }.launch("load")
+ .onResourceError(errorHandler::dispatch)
+ .launch("load")
}
private fun checkIsNoContent(
@@ -267,7 +238,8 @@ class GradeStatisticsPresenter @Inject constructor(
items.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0
}
GradeStatisticsItem.DataType.POINTS -> {
- items.firstOrNull()?.points?.let { points -> points.student == .0 && points.others == .0 } ?: false
+ items.firstOrNull()?.points?.let { points -> points.student == .0 && points.others == .0 }
+ ?: false
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt
index 933633dca..b07570cb2 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt
@@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.grade.summary
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
@@ -8,9 +8,6 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.ui.modules.grade.GradeSubject
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -37,56 +34,40 @@ class GradeSummaryPresenter @Inject constructor(
}
private fun loadData(semesterId: Int, forceRefresh: Boolean) {
- Timber.i("Loading grade summary started")
-
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh)
- }.onEach {
- Timber.d("Loading grade summary status: ${it.status}, data: ${it.data != null}")
- when (it.status) {
- Status.LOADING -> {
- val items = createGradeSummaryItems(it.data.orEmpty())
- if (items.isNotEmpty()) {
- Timber.i("Loading grade summary result: load cached data")
- view?.run {
- enableSwipe(true)
- showRefresh(true)
- showProgress(false)
- showEmpty(false)
- showContent(true)
- updateData(items)
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading grade summary result: Success")
- val items = createGradeSummaryItems(it.data!!)
- view?.run {
- showEmpty(items.isEmpty())
- showContent(items.isNotEmpty())
- showErrorView(false)
- updateData(items)
- }
- analytics.logEvent(
- "load_data",
- "type" to "grade_summary",
- "items" to it.data.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading grade summary result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load grade summary", showData = true)
+ .mapResourceData { createGradeSummaryItems(it) }
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showContent(it.isNotEmpty())
+ showEmpty(it.isEmpty())
+ updateData(it)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
- notifyParentDataLoaded(semesterId)
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "grade_summary",
+ "items" to it.size
+ )
}
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ enableSwipe(true)
+ showRefresh(false)
+ showProgress(false)
+ notifyParentDataLoaded(semesterId)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@@ -153,9 +134,9 @@ class GradeSummaryPresenter @Inject constructor(
private fun checkEmpty(gradeSummary: GradeSubject): Boolean {
return gradeSummary.run {
summary.finalGrade.isBlank()
- && summary.predictedGrade.isBlank()
- && average == .0
- && points.isBlank()
+ && summary.predictedGrade.isBlank()
+ && average == .0
+ && points.isBlank()
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt
index d7d5d7cb9..2ac552b41 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt
@@ -1,21 +1,13 @@
package io.github.wulkanowy.ui.modules.homework
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.repositories.HomeworkRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
-import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
-import io.github.wulkanowy.utils.isHolidays
-import io.github.wulkanowy.utils.monday
-import io.github.wulkanowy.utils.nextOrSameSchoolDay
-import io.github.wulkanowy.utils.sunday
-import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
@@ -89,61 +81,59 @@ class HomeworkPresenter @Inject constructor(
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
- }.catch {
- Timber.i("Loading semester result: An exception occurred")
- }.onEach {
- baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
- currentDate = baseDate
- reloadNavigation()
- }.launch("holidays")
+ }
+ .catch { Timber.i("Loading semester result: An exception occurred") }
+ .onEach {
+ baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
+ currentDate = baseDate
+ reloadNavigation()
+ }
+ .launch("holidays")
}
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading homework data started")
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
- homeworkRepository.getHomework(student, semester, currentDate, currentDate, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- if (!it.data.isNullOrEmpty()) {
- view?.run {
- enableSwipe(true)
- showRefresh(true)
- showProgress(false)
- showContent(true)
- updateData(createHomeworkItem(it.data))
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading homework result: Success")
- view?.apply {
- updateData(createHomeworkItem(it.data!!))
- showEmpty(it.data.isEmpty())
- showErrorView(false)
- showContent(it.data.isNotEmpty())
- }
- analytics.logEvent(
- "load_data",
- "type" to "homework",
- "items" to it.data!!.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading homework result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ homeworkRepository.getHomework(
+ student = student,
+ semester = semester,
+ start = currentDate,
+ end = currentDate,
+ forceRefresh = forceRefresh
+ )
+ }
+ .logResourceStatus("loading homework")
+ .mapResourceData { createHomeworkItem(it) }
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showContent(it.isNotEmpty())
+ showEmpty(it.isEmpty())
+ updateData(it)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "homework",
+ "items" to it.size
+ )
}
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showRefresh(false)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@@ -159,9 +149,10 @@ class HomeworkPresenter @Inject constructor(
private fun createHomeworkItem(items: List): List> {
return items.groupBy { it.date }.toSortedMap().map { (date, exams) ->
- listOf(HomeworkItem(date, HomeworkItem.ViewType.HEADER)) + exams.reversed().map { exam ->
- HomeworkItem(exam, HomeworkItem.ViewType.ITEM)
- }
+ listOf(HomeworkItem(date, HomeworkItem.ViewType.HEADER)) + exams.reversed()
+ .map { exam ->
+ HomeworkItem(exam, HomeworkItem.ViewType.ITEM)
+ }
}.flatten()
}
@@ -184,8 +175,10 @@ class HomeworkPresenter @Inject constructor(
view?.apply {
showPreButton(!currentDate.minusDays(7).isHolidays)
showNextButton(!currentDate.plusDays(7).isHolidays)
- updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " +
- currentDate.sunday.toFormattedString("dd.MM"))
+ updateNavigationWeek(
+ "${currentDate.monday.toFormattedString("dd.MM")} - " +
+ currentDate.sunday.toFormattedString("dd.MM")
+ )
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddPresenter.kt
index 3639c2fef..a21f6aef7 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/add/HomeworkAddPresenter.kt
@@ -1,15 +1,16 @@
package io.github.wulkanowy.ui.modules.homework.add
-import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Homework
+import io.github.wulkanowy.data.logResourceStatus
+import io.github.wulkanowy.data.onResourceError
+import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.HomeworkRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.toLocalDate
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.time.LocalDate
import javax.inject.Inject
@@ -55,7 +56,7 @@ class HomeworkAddPresenter @Inject constructor(
}
private fun saveHomework(subject: String, teacher: String, date: LocalDate, content: String) {
- flowWithResource {
+ resourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
val entryDate = LocalDate.now()
@@ -72,21 +73,15 @@ class HomeworkAddPresenter @Inject constructor(
attachments = emptyList(),
).apply { isAddedByUser = true }
)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Homework insert start")
- Status.SUCCESS -> {
- Timber.i("Homework insert: Success")
- view?.run {
- showSuccessMessage()
- closeDialog()
- }
- }
- Status.ERROR -> {
- Timber.i("Homework insert result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("homework insert")
+ .onResourceSuccess {
+ view?.run {
+ showSuccessMessage()
+ closeDialog()
}
}
- }.launch("add_homework")
+ .onResourceError(errorHandler::dispatch)
+ .launch("add_homework")
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt
index ea9b47a05..e76df6bd0 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/details/HomeworkDetailsPresenter.kt
@@ -1,15 +1,16 @@
package io.github.wulkanowy.ui.modules.homework.details
-import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Homework
+import io.github.wulkanowy.data.logResourceStatus
+import io.github.wulkanowy.data.onResourceError
+import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.HomeworkRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.flowWithResource
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -34,38 +35,26 @@ class HomeworkDetailsPresenter @Inject constructor(
}
fun deleteHomework(homework: Homework) {
- flowWithResource { homeworkRepository.deleteHomework(homework) }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Homework delete start")
- Status.SUCCESS -> {
- Timber.i("Homework delete: Success")
- view?.run {
- showMessage(homeworkDeleteSuccess)
- closeDialog()
- }
- }
- Status.ERROR -> {
- Timber.i("Homework delete result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ resourceFlow { homeworkRepository.deleteHomework(homework) }
+ .logResourceStatus("homework delete")
+ .onResourceSuccess {
+ view?.run {
+ showMessage(homeworkDeleteSuccess)
+ closeDialog()
}
}
- }.launch("delete")
+ .onResourceError(errorHandler::dispatch)
+ .launch("delete")
}
fun toggleDone(homework: Homework) {
- flowWithResource { homeworkRepository.toggleDone(homework) }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Homework details update start")
- Status.SUCCESS -> {
- Timber.i("Homework details update: Success")
- view?.updateMarkAsDoneLabel(homework.isDone)
- analytics.logEvent("homework_mark_as_done")
- }
- Status.ERROR -> {
- Timber.i("Homework details update result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
+ resourceFlow { homeworkRepository.toggleDone(homework) }
+ .logResourceStatus("homework details update")
+ .onResourceSuccess {
+ view?.updateMarkAsDoneLabel(homework.isDone)
+ analytics.logEvent("homework_mark_as_done")
}
- }.launch("toggle")
+ .onResourceError(errorHandler::dispatch)
+ .launch("toggle")
}
}
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 3543a3041..1b42c6c52 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
@@ -1,15 +1,16 @@
package io.github.wulkanowy.ui.modules.login.advanced
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.data.logResourceStatus
+import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
@@ -129,20 +130,20 @@ class LoginAdvancedPresenter @Inject constructor(
fun onSignInClick() {
if (!validateCredentials()) return
- flowWithResource { getStudentsAppropriatesToLoginType() }.onEach {
- when (it.status) {
- Status.LOADING -> view?.run {
- Timber.i("Login started")
- hideSoftKeyboard()
- showProgress(true)
- showContent(false)
- }
- Status.SUCCESS -> {
- Timber.i("Login result: Success")
- analytics.logEvent(
- "registration_form",
+ resourceFlow { getStudentsAppropriatesToLoginType() }
+ .logResourceStatus("login")
+ .onEach {
+ when (it) {
+ is Resource.Loading -> view?.run {
+ hideSoftKeyboard()
+ showProgress(true)
+ showContent(false)
+ }
+ is Resource.Success -> {
+ analytics.logEvent(
+ "registration_form",
"success" to true,
- "students" to it.data!!.size,
+ "students" to it.data.size,
"error" to "No error"
)
val loginData = LoginData(
@@ -154,23 +155,22 @@ class LoginAdvancedPresenter @Inject constructor(
0 -> view?.navigateToSymbol(loginData)
else -> view?.navigateToStudentSelect(it.data)
}
+ }
+ is Resource.Error -> {
+ analytics.logEvent(
+ "registration_form",
+ "success" to false, "students" to -1,
+ "error" to it.error.message.ifNullOrBlank { "No message" }
+ )
+ loginErrorHandler.dispatch(it.error)
+ }
}
- Status.ERROR -> {
- Timber.i("Login result: An exception occurred")
- analytics.logEvent(
- "registration_form",
- "success" to false, "students" to -1,
- "error" to it.error!!.message.ifNullOrBlank { "No message" }
- )
- loginErrorHandler.dispatch(it.error)
+ }.onResourceNotLoading {
+ view?.apply {
+ showProgress(false)
+ showContent(true)
}
- }
- }.afterLoading {
- view?.apply {
- showProgress(false)
- showContent(true)
- }
- }.launch("login")
+ }.launch("login")
}
private suspend fun getStudentsAppropriatesToLoginType(): List {
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 49be6fbb6..b4291ff47 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
@@ -1,16 +1,13 @@
package io.github.wulkanowy.ui.modules.login.form
import androidx.core.net.toUri
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.net.URL
import javax.inject.Inject
@@ -75,7 +72,7 @@ class LoginFormPresenter @Inject constructor(
val username = view?.formUsernameValue.orEmpty().trim()
if ("@" in username && "@vulcan" !in username) {
- val hosts = view?.getHostsValues().orEmpty().map { it.toUri().host to it }.toMap()
+ val hosts = view?.getHostsValues().orEmpty().associateBy { it.toUri().host }
val usernameHost = username.substringAfter("@")
hosts[usernameHost]?.let {
@@ -95,54 +92,54 @@ class LoginFormPresenter @Inject constructor(
if (!validateCredentials(email, password, host)) return
- flowWithResource {
+ resourceFlow {
studentRepository.getStudentsScrapper(
email = email,
password = password,
scrapperBaseUrl = host,
symbol = symbol
)
- }.onEach {
- when (it.status) {
- Status.LOADING -> view?.run {
- Timber.i("Login started")
+ }
+ .logResourceStatus("login")
+ .onResourceLoading {
+ view?.run {
hideSoftKeyboard()
showProgress(true)
showContent(false)
}
- Status.SUCCESS -> {
- Timber.i("Login result: Success")
- analytics.logEvent(
- "registration_form",
- "success" to true,
- "students" to it.data!!.size,
- "scrapperBaseUrl" to host,
- "error" to "No error"
- )
- when (it.data.size) {
- 0 -> view?.navigateToSymbol(LoginData(email, password, host))
- else -> view?.navigateToStudentSelect(it.data)
- }
+ }
+ .onResourceSuccess {
+ when (it.size) {
+ 0 -> view?.navigateToSymbol(LoginData(email, password, host))
+ else -> view?.navigateToStudentSelect(it)
}
- Status.ERROR -> {
- Timber.i("Login result: An exception occurred")
- analytics.logEvent(
- "registration_form",
- "success" to false,
- "students" to -1,
- "scrapperBaseUrl" to host,
- "error" to it.error!!.message.ifNullOrBlank { "No message" })
- loginErrorHandler.dispatch(it.error)
- lastError = it.error
- view?.showContact(true)
+ analytics.logEvent(
+ "registration_form",
+ "success" to true,
+ "students" to it.size,
+ "scrapperBaseUrl" to host,
+ "error" to "No error"
+ )
+ }
+ .onResourceNotLoading {
+ view?.apply {
+ showProgress(false)
+ showContent(true)
}
}
- }.afterLoading {
- view?.apply {
- showProgress(false)
- showContent(true)
+ .onResourceError {
+ loginErrorHandler.dispatch(it)
+ lastError = it
+ view?.showContact(true)
+ analytics.logEvent(
+ "registration_form",
+ "success" to false,
+ "students" to -1,
+ "scrapperBaseUrl" to host,
+ "error" to it.message.ifNullOrBlank { "No message" }
+ )
}
- }.launch("login")
+ .launch("login")
}
fun onFaqClick() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt
index 271e8a8a0..3d0493012 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverPresenter.kt
@@ -1,12 +1,12 @@
package io.github.wulkanowy.ui.modules.login.recover
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.Resource
+import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.repositories.RecoverRepository
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
@@ -57,24 +57,28 @@ class LoginRecoverPresenter @Inject constructor(
if (!validateInput(username, host)) return
- flowWithResource { recoverRepository.getReCaptchaSiteKey(host, symbol.ifBlank { "Default" }) }.onEach {
- when (it.status) {
- Status.LOADING -> view?.run {
+ resourceFlow {
+ recoverRepository.getReCaptchaSiteKey(
+ host,
+ symbol.ifBlank { "Default" })
+ }.onEach {
+ when (it) {
+ is Resource.Loading -> view?.run {
hideSoftKeyboard()
showRecoverForm(false)
showProgress(true)
showErrorView(false)
showCaptcha(false)
}
- Status.SUCCESS -> view?.run {
- loadReCaptcha(url = it.data!!.first, siteKey = it.data.second)
+ is Resource.Success -> view?.run {
+ loadReCaptcha(url = it.data.first, siteKey = it.data.second)
showProgress(false)
showErrorView(false)
showCaptcha(true)
}
- Status.ERROR -> {
+ is Resource.Error -> {
Timber.i("Obtain captcha site key result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ errorHandler.dispatch(it.error)
}
}
}.launch("captcha")
@@ -101,26 +105,43 @@ class LoginRecoverPresenter @Inject constructor(
val host = view?.recoverHostValue.orEmpty()
val symbol = view?.formHostSymbol.ifNullOrBlank { "Default" }
- flowWithResource { recoverRepository.sendRecoverRequest(host, symbol, username, reCaptchaResponse) }.onEach {
- when (it.status) {
- Status.LOADING -> view?.run {
+ resourceFlow {
+ recoverRepository.sendRecoverRequest(
+ host,
+ symbol,
+ username,
+ reCaptchaResponse
+ )
+ }.onEach {
+ when (it) {
+ is Resource.Loading -> view?.run {
showProgress(true)
showRecoverForm(false)
showCaptcha(false)
}
- Status.SUCCESS -> view?.run {
+ is Resource.Success -> view?.run {
showSuccessView(true)
- setSuccessTitle(it.data!!.substringBefore(". "))
+ setSuccessTitle(it.data.substringBefore(". "))
setSuccessMessage(it.data.substringAfter(". "))
- analytics.logEvent("account_recover", "register" to host, "symbol" to symbol, "success" to true)
+ analytics.logEvent(
+ "account_recover",
+ "register" to host,
+ "symbol" to symbol,
+ "success" to true
+ )
}
- Status.ERROR -> {
+ is Resource.Error -> {
Timber.i("Send recover request result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- analytics.logEvent("account_recover", "register" to host, "symbol" to symbol, "success" to false)
+ errorHandler.dispatch(it.error)
+ analytics.logEvent(
+ "account_recover",
+ "register" to host,
+ "symbol" to symbol,
+ "success" to false
+ )
}
}
- }.afterLoading {
+ }.onResourceNotLoading {
view?.showProgress(false)
}.launch("verified")
}
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 71c60e62d..3455b3cf1 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
@@ -1,14 +1,15 @@
package io.github.wulkanowy.ui.modules.login.studentselect
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
@@ -66,16 +67,16 @@ class LoginStudentSelectPresenter @Inject constructor(
private fun loadData(studentsWithSemesters: List) {
resetSelectedState()
- flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.d("Login student select students load started")
- Status.SUCCESS -> view?.updateData(studentsWithSemesters.map { studentWithSemesters ->
- studentWithSemesters to it.data!!.any { item ->
+ resourceFlow { studentRepository.getSavedStudents(false) }.onEach {
+ when (it) {
+ is Resource.Loading -> Timber.d("Login student select students load started")
+ is Resource.Success -> view?.updateData(studentsWithSemesters.map { studentWithSemesters ->
+ studentWithSemesters to it.data.any { item ->
compareStudents(studentWithSemesters.student, item.student)
}
})
- Status.ERROR -> {
- errorHandler.dispatch(it.error!!)
+ is Resource.Error -> {
+ errorHandler.dispatch(it.error)
lastError = it.error
view?.updateData(studentsWithSemesters.map { student -> student to false })
}
@@ -89,29 +90,27 @@ class LoginStudentSelectPresenter @Inject constructor(
}
private fun registerStudents(studentsWithSemesters: List) {
- flowWithResource { studentRepository.saveStudents(studentsWithSemesters) }
+ resourceFlow { studentRepository.saveStudents(studentsWithSemesters) }
+ .logResourceStatus("registration")
.onEach {
- when (it.status) {
- Status.LOADING -> view?.run {
- Timber.i("Registration started")
+ when (it) {
+ is Resource.Loading -> view?.run {
showProgress(true)
showContent(false)
}
- Status.SUCCESS -> {
- Timber.i("Registration result: Success")
+ is Resource.Success -> {
syncManager.startOneTimeSyncWorker(quiet = true)
view?.openMainView()
logRegisterEvent(studentsWithSemesters)
}
- Status.ERROR -> {
- Timber.i("Registration result: An exception occurred ")
+ is Resource.Error -> {
view?.apply {
showProgress(false)
showContent(true)
showContact(true)
}
lastError = it.error
- loginErrorHandler.dispatch(it.error!!)
+ loginErrorHandler.dispatch(it.error)
logRegisterEvent(studentsWithSemesters, it.error)
}
}
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 7e195893f..691cd4481 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
@@ -1,13 +1,13 @@
package io.github.wulkanowy.ui.modules.login.symbol
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.Resource
+import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
@@ -45,7 +45,7 @@ class LoginSymbolPresenter @Inject constructor(
return
}
- flowWithResource {
+ resourceFlow {
studentRepository.getStudentsScrapper(
email = loginData.login,
password = loginData.password,
@@ -53,15 +53,15 @@ class LoginSymbolPresenter @Inject constructor(
symbol = symbol,
)
}.onEach {
- when (it.status) {
- Status.LOADING -> view?.run {
+ when (it) {
+ is Resource.Loading -> view?.run {
Timber.i("Login with symbol started")
hideSoftKeyboard()
showProgress(true)
showContent(false)
}
- Status.SUCCESS -> {
- when (it.data?.size) {
+ is Resource.Success -> {
+ when (it.data.size) {
0 -> {
Timber.i("Login with symbol result: Empty student list")
view?.run {
@@ -77,13 +77,13 @@ class LoginSymbolPresenter @Inject constructor(
analytics.logEvent(
"registration_symbol",
"success" to true,
- "students" to it.data!!.size,
+ "students" to it.data.size,
"scrapperBaseUrl" to loginData.baseUrl,
"symbol" to symbol,
"error" to "No error"
)
}
- Status.ERROR -> {
+ is Resource.Error -> {
Timber.i("Login with symbol result: An exception occurred")
analytics.logEvent(
"registration_symbol",
@@ -91,14 +91,14 @@ class LoginSymbolPresenter @Inject constructor(
"students" to -1,
"scrapperBaseUrl" to loginData.baseUrl,
"symbol" to symbol,
- "error" to it.error!!.message.ifNullOrBlank { "No message" }
+ "error" to it.error.message.ifNullOrBlank { "No message" }
)
loginErrorHandler.dispatch(it.error)
lastError = it.error
view?.showContact(true)
}
}
- }.afterLoading {
+ }.onResourceNotLoading {
view?.apply {
showProgress(false)
showContent(true)
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 fd0598d8f..6f5c8e740 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
@@ -1,14 +1,11 @@
package io.github.wulkanowy.ui.modules.luckynumber
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -34,47 +31,45 @@ class LuckyNumberPresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
luckyNumberRepository.getLuckyNumber(student, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading lucky number started")
- Status.SUCCESS -> {
- if (it.data != null) {
- Timber.i("Loading lucky number result: Success")
- view?.apply {
- updateData(it.data)
- showContent(true)
- showEmpty(false)
- showErrorView(false)
- }
- analytics.logEvent(
- "load_item",
- "type" to "lucky_number",
- "number" to it.data.luckyNumber
- )
- } else {
- Timber.i("Loading lucky number result: No lucky number found")
- view?.run {
- showContent(false)
- showEmpty(true)
- showErrorView(false)
- }
+ }
+ .logResourceStatus("load lucky number")
+ .onResourceData {
+ if (it != null) {
+ view?.apply {
+ updateData(it)
+ showContent(true)
+ showEmpty(false)
+ showErrorView(false)
+ }
+ } else {
+ view?.run {
+ showContent(false)
+ showEmpty(true)
+ showErrorView(false)
}
}
- Status.ERROR -> {
- Timber.i("Loading lucky number result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .onResourceSuccess {
+ if (it != null) {
+ analytics.logEvent(
+ "load_item",
+ "type" to "lucky_number",
+ "number" to it.luckyNumber
+ )
}
}
- }.afterLoading {
- view?.run {
- hideRefresh()
- showProgress(false)
- enableSwipe(true)
+ .onResourceNotLoading {
+ view?.run {
+ hideRefresh()
+ showProgress(false)
+ enableSwipe(true)
+ }
}
- }.launch()
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt
index c45cb69a7..fc753950b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumber/history/LuckyNumberHistoryPresenter.kt
@@ -1,24 +1,12 @@
package io.github.wulkanowy.ui.modules.luckynumber.history
-import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
-import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
-import io.github.wulkanowy.utils.isHolidays
-import io.github.wulkanowy.utils.monday
-import io.github.wulkanowy.utils.previousOrSameSchoolDay
-import io.github.wulkanowy.utils.sunday
-import io.github.wulkanowy.utils.toFormattedString
-import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.flow.onEach
+import io.github.wulkanowy.utils.*
+import kotlinx.coroutines.flow.*
import timber.log.Timber
import java.time.LocalDate
import javax.inject.Inject
@@ -52,55 +40,51 @@ class LuckyNumberHistoryPresenter @Inject constructor(
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
- }.catch {
- Timber.i("Loading semester result: An exception occurred")
- }.onEach {
- currentDate = currentDate.getLastSchoolDayIfHoliday(it.schoolYear)
- reloadNavigation()
- }.launch("holidays")
+ }
+ .catch { Timber.i("Loading semester result: An exception occurred") }
+ .onEach {
+ currentDate = currentDate.getLastSchoolDayIfHoliday(it.schoolYear)
+ reloadNavigation()
+ }
+ .launch("holidays")
}
private fun loadData() {
- flowWithResource {
+ flow {
val student = studentRepository.getCurrentStudent()
- luckyNumberRepository.getLuckyNumberHistory(student, currentDate.monday, currentDate.sunday)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading lucky number history started")
- Status.SUCCESS -> {
- if (!it.data?.first().isNullOrEmpty()) {
- Timber.i("Loading lucky number result: Success")
- view?.apply {
- updateData(it.data!!.first())
- showContent(true)
- showEmpty(false)
- showErrorView(false)
- showProgress(false)
- }
- analytics.logEvent(
- "load_items",
- "type" to "lucky_number_history",
- "numbers" to it.data
- )
- } else {
- Timber.i("Loading lucky number history result: No lucky numbers found")
- view?.run {
- showContent(false)
- showEmpty(true)
- showErrorView(false)
- }
+ emitAll(
+ luckyNumberRepository.getLuckyNumberHistory(
+ student = student,
+ start = currentDate.monday,
+ end = currentDate.sunday
+ )
+ )
+ }
+ .onEach {
+ if (!it.isNullOrEmpty()) {
+ view?.apply {
+ updateData(it)
+ showContent(true)
+ showEmpty(false)
+ showErrorView(false)
+ showProgress(false)
+ }
+ } else {
+ view?.run {
+ showContent(false)
+ showEmpty(true)
+ showErrorView(false)
+ showProgress(false)
}
}
- Status.ERROR -> {
- Timber.i("Loading lucky number history result: An exception occurred")
- errorHandler.dispatch(it.error!!)
- }
+
+ analytics.logEvent(
+ "load_items",
+ "type" to "lucky_number_history",
+ )
}
- }.afterLoading {
- view?.run {
- showProgress(false)
- }
- }.launch()
+ .catch { errorHandler.dispatch(it) }
+ .launchIn(presenterScope)
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@@ -143,8 +127,10 @@ class LuckyNumberHistoryPresenter @Inject constructor(
view?.apply {
showPreButton(!currentDate.minusDays(7).isHolidays)
showNextButton(!currentDate.plusDays(7).isHolidays)
- updateNavigationWeek("${currentDate.monday.toFormattedString("dd.MM")} - " +
- currentDate.sunday.toFormattedString("dd.MM"))
+ updateNavigationWeek(
+ "${currentDate.monday.toFormattedString("dd.MM")} - " +
+ currentDate.sunday.toFormattedString("dd.MM")
+ )
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt
index 5b6af69ac..cac648da8 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetConfigurePresenter.kt
@@ -1,14 +1,14 @@
package io.github.wulkanowy.ui.modules.luckynumberwidget
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getStudentWidgetKey
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getThemeWidgetKey
-import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -47,16 +47,15 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
}
private fun loadData() {
- flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.d("Lucky number widget configure students data load")
- Status.SUCCESS -> {
+ resourceFlow { studentRepository.getSavedStudents(false) }.onEach {
+ when (it) {
+ is Resource.Loading -> Timber.d("Lucky number widget configure students data load")
+ is Resource.Success -> {
val selectedStudentId = appWidgetId?.let { id ->
sharedPref.getLong(getStudentWidgetKey(id), 0)
} ?: -1
-
when {
- it.data!!.isEmpty() -> view?.openLoginView()
+ it.data.isEmpty() -> view?.openLoginView()
it.data.size == 1 -> {
selectedStudent = it.data.single().student
view?.showThemeDialog()
@@ -64,7 +63,7 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
else -> view?.updateData(it.data, selectedStudentId)
}
}
- Status.ERROR -> errorHandler.dispatch(it.error!!)
+ is Resource.Error -> errorHandler.dispatch(it.error)
}
}.launch()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt
index 2b2d18fa0..e016c07e6 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt
@@ -13,14 +13,17 @@ import android.view.View.VISIBLE
import android.widget.RemoteViews
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
+import io.github.wulkanowy.data.Resource
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.SharedPrefProvider
+import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.PendingIntentCompat
-import io.github.wulkanowy.utils.toFirstResult
import kotlinx.coroutines.runBlocking
import timber.log.Timber
import javax.inject.Inject
@@ -66,12 +69,16 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
)
+ if (luckyNumber is Resource.Error) {
+ Timber.e("Error loading lucky number for widget", luckyNumber.error)
+ }
+
val remoteView =
RemoteViews(context.packageName, getCorrectLayoutId(appWidgetId, context))
.apply {
setTextViewText(
R.id.luckyNumberWidgetNumber,
- luckyNumber?.luckyNumber?.toString() ?: "#"
+ luckyNumber.dataOrNull?.toString() ?: "#"
)
setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent)
}
@@ -167,14 +174,17 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
else -> null
}
- currentStudent?.let {
- luckyNumberRepository.getLuckyNumber(it, false).toFirstResult().data
+ if (currentStudent != null) {
+ luckyNumberRepository.getLuckyNumber(currentStudent, forceRefresh = false)
+ .toFirstResult()
+ } else {
+ Resource.Success(null)
}
} catch (e: Exception) {
if (e.cause !is NoCurrentStudentException) {
Timber.e(e, "An error has occurred in lucky number provider")
}
- null
+ Resource.Error(e)
}
}
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 821b1e6ff..e01497b9a 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
@@ -1,9 +1,12 @@
package io.github.wulkanowy.ui.modules.main
-import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.data.logResourceStatus
+import io.github.wulkanowy.data.onResourceError
+import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.BaseView
@@ -16,8 +19,6 @@ import io.github.wulkanowy.ui.modules.message.MessageView
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersView
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.flowWithResource
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.time.Duration
import java.time.Instant
@@ -75,20 +76,14 @@ class MainPresenter @Inject constructor(
return
}
- flowWithResource { studentRepository.getSavedStudents(false) }
- .onEach { resource ->
- when (resource.status) {
- Status.LOADING -> Timber.i("Loading student avatar data started")
- Status.SUCCESS -> {
- studentsWitSemesters = resource.data
- showCurrentStudentAvatar()
- }
- Status.ERROR -> {
- Timber.i("Loading student avatar result: An exception occurred")
- errorHandler.dispatch(resource.error!!)
- }
- }
- }.launch("avatar")
+ resourceFlow { studentRepository.getSavedStudents(false) }
+ .logResourceStatus("load student avatar")
+ .onResourceSuccess {
+ studentsWitSemesters = it
+ showCurrentStudentAvatar()
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch("avatar")
}
fun onViewChange(destinationView: BaseView) {
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 eb33ee6ea..39c337bf2 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,7 +1,7 @@
package io.github.wulkanowy.ui.modules.message.preview
import android.annotation.SuppressLint
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.enums.MessageFolder
@@ -10,11 +10,8 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
-import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.toFormattedString
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@@ -53,44 +50,43 @@ class MessagePreviewPresenter @Inject constructor(
view?.showErrorDetailsDialog(lastError)
}
- private fun loadData(message: Message) {
- flowWithResourceIn {
- val student = studentRepository.getStudentById(message.studentId)
- messageRepository.getMessage(student, message, true)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading message ${message.messageId} preview started")
- Status.SUCCESS -> {
- Timber.i("Loading message ${message.messageId} preview result: Success ")
- if (it.data != null) {
- this@MessagePreviewPresenter.message = it.data.message
- this@MessagePreviewPresenter.attachments = it.data.attachments
- view?.apply {
- setMessageWithAttachment(it.data)
- showContent(true)
- initOptions()
- }
- analytics.logEvent(
- "load_item",
- "type" to "message_preview",
- "length" to it.data.message.content.length
- )
- } else {
- view?.run {
- showMessage(messageNotExists)
- popView()
- }
+ private fun loadData(messageToLoad: Message) {
+ flatResourceFlow {
+ val student = studentRepository.getStudentById(messageToLoad.studentId)
+ messageRepository.getMessage(student, messageToLoad, true)
+ }
+ .logResourceStatus("message ${messageToLoad.messageId} preview")
+ .onResourceData {
+ if (it != null) {
+ message = it.message
+ attachments = it.attachments
+ view?.apply {
+ setMessageWithAttachment(it)
+ showContent(true)
+ initOptions()
+ }
+ } else {
+ view?.run {
+ showMessage(messageNotExists)
+ popView()
}
}
- Status.ERROR -> {
- Timber.i("Loading message ${message.messageId} preview result: An exception occurred ")
- retryCallback = { onMessageLoadRetry(message) }
- errorHandler.dispatch(it.error!!)
+ }
+ .onResourceSuccess {
+ if (it != null) {
+ analytics.logEvent(
+ "load_item",
+ "type" to "message_preview",
+ "length" to it.message.content.length
+ )
}
}
- }.afterLoading {
- view?.showProgress(false)
- }.launch()
+ .onResourceNotLoading { view?.showProgress(false) }
+ .onResourceError {
+ retryCallback = { onMessageLoadRetry(messageToLoad) }
+ errorHandler.dispatch(it)
+ }
+ .launch()
}
fun onReply(): Boolean {
@@ -176,28 +172,26 @@ class MessagePreviewPresenter @Inject constructor(
showErrorView(false)
}
- flowWithResource {
- val student = studentRepository.getCurrentStudent()
- messageRepository.deleteMessage(student, message!!)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.d("Message ${message?.id} delete started")
- Status.SUCCESS -> {
- Timber.d("Message ${message?.id} delete success")
+ Timber.i("Delete message ${message?.id}")
+
+ presenterScope.launch {
+ runCatching {
+ val student = studentRepository.getCurrentStudent()
+ messageRepository.deleteMessage(student, message!!)
+ }
+ .onFailure {
+ retryCallback = { onMessageDelete() }
+ errorHandler.dispatch(it)
+ }
+ .onSuccess {
view?.run {
showMessage(deleteMessageSuccessString)
popView()
}
}
- Status.ERROR -> {
- Timber.d("Message ${message?.id} delete failed")
- retryCallback = { onMessageDelete() }
- errorHandler.dispatch(it.error!!)
- }
- }
- }.afterLoading {
+
view?.showProgress(false)
- }.launch("delete")
+ }
}
private fun showErrorViewOnError(message: String, error: Throwable) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt
index 5e961efc3..e5770955a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt
@@ -1,25 +1,20 @@
package io.github.wulkanowy.ui.modules.message.send
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient
+import io.github.wulkanowy.data.logResourceStatus
+import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.pojos.MessageDraft
-import io.github.wulkanowy.data.repositories.MessageRepository
-import io.github.wulkanowy.data.repositories.PreferencesRepository
-import io.github.wulkanowy.data.repositories.RecipientRepository
-import io.github.wulkanowy.data.repositories.ReportingUnitRepository
-import io.github.wulkanowy.data.repositories.SemesterRepository
-import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.repositories.*
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.onEach
@@ -55,10 +50,12 @@ class SendMessagePresenter @Inject constructor(
setContent(it)
}
message?.let {
- setSubject(when (reply) {
- true -> "Re: "
- else -> "FW: "
- } + message.subject)
+ setSubject(
+ when (reply) {
+ true -> "Re: "
+ else -> "FW: "
+ } + message.subject
+ )
if (preferencesRepository.fillMessageContent || reply != true) {
setContent(
when (reply) {
@@ -67,7 +64,8 @@ class SendMessagePresenter @Inject constructor(
} + when (message.sender.isNotEmpty()) {
true -> "Od: ${message.sender}\n"
false -> "Do: ${message.recipient}\n"
- } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}")
+ } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}"
+ )
}
}
}
@@ -111,7 +109,7 @@ class SendMessagePresenter @Inject constructor(
}
private fun loadData(message: Message?, reply: Boolean?) {
- flowWithResource {
+ resourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
val unit = reportingUnitRepository.getReportingUnit(student, semester.unitId)
@@ -125,58 +123,64 @@ class SendMessagePresenter @Inject constructor(
Timber.i("Loading message recipients started")
val messageRecipients = when {
- message != null && reply == true -> recipientRepository.getMessageRecipients(student, message)
+ message != null && reply == true -> recipientRepository.getMessageRecipients(
+ student,
+ message
+ )
else -> emptyList()
}.let { createChips(it) }
- Timber.i("Loaded message recipients to reply result: Success, fetched %d recipients", messageRecipients.size)
+ Timber.i(
+ "Loaded message recipients to reply result: Success, fetched %d recipients",
+ messageRecipients.size
+ )
Triple(unit, recipients, messageRecipients)
- }.onEach {
- when (it.status) {
- Status.LOADING -> view?.run {
- Timber.i("Loading recipients started")
- showProgress(true)
- showContent(false)
- }
- Status.SUCCESS -> it.data!!.let { (reportingUnit, recipientChips, selectedRecipientChips) ->
- view?.run {
- if (reportingUnit != null) {
- setReportingUnit(reportingUnit)
- setRecipients(recipientChips)
- if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(selectedRecipientChips)
- showContent(true)
- } else {
- Timber.i("Loading recipients result: Can't find the reporting unit")
- view?.showEmpty(true)
+ }
+ .logResourceStatus("load recipients")
+ .onEach {
+ when (it) {
+ is Resource.Loading -> view?.run {
+ showProgress(true)
+ showContent(false)
+ }
+ is Resource.Success -> it.data.let { (reportingUnit, recipientChips, selectedRecipientChips) ->
+ view?.run {
+ if (reportingUnit != null) {
+ setReportingUnit(reportingUnit)
+ setRecipients(recipientChips)
+ if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(
+ selectedRecipientChips
+ )
+ showContent(true)
+ } else {
+ Timber.i("Loading recipients result: Can't find the reporting unit")
+ view?.showEmpty(true)
+ }
}
}
+ is Resource.Error -> {
+ view?.showContent(true)
+ errorHandler.dispatch(it.error)
+ }
}
- Status.ERROR -> {
- Timber.i("Loading recipients result: An exception occurred")
- view?.showContent(true)
- errorHandler.dispatch(it.error!!)
- }
- }
- }.afterLoading {
- view?.run { showProgress(false) }
- }.launch()
+ }.onResourceNotLoading {
+ view?.run { showProgress(false) }
+ }.launch()
}
private fun sendMessage(subject: String, content: String, recipients: List) {
- flowWithResource {
+ resourceFlow {
val student = studentRepository.getCurrentStudent()
messageRepository.sendMessage(student, subject, content, recipients)
- }.onEach {
- when (it.status) {
- Status.LOADING -> view?.run {
- Timber.i("Sending message started")
+ }.logResourceStatus("sending message").onEach {
+ when (it) {
+ is Resource.Loading -> view?.run {
showSoftInput(false)
showContent(false)
showProgress(true)
showActionBar(false)
}
- Status.SUCCESS -> {
- Timber.i("Sending message result: Success")
+ is Resource.Success -> {
view?.clearDraft()
view?.run {
showMessage(messageSuccess)
@@ -184,14 +188,13 @@ class SendMessagePresenter @Inject constructor(
}
analytics.logEvent("send_message", "recipients" to recipients.size)
}
- Status.ERROR -> {
- Timber.i("Sending message result: An exception occurred")
+ is Resource.Error -> {
view?.run {
showContent(true)
showProgress(false)
showActionBar(true)
}
- errorHandler.dispatch(it.error!!)
+ errorHandler.dispatch(it.error)
}
}
}.launch("send")
@@ -259,7 +262,8 @@ class SendMessagePresenter @Inject constructor(
}
fun getRecipientsNames(): String {
- return messageRepository.draftMessage?.recipients.orEmpty().joinToString { it.recipient.name }
+ return messageRepository.draftMessage?.recipients.orEmpty()
+ .joinToString { it.recipient.name }
}
fun clearDraft() {
@@ -267,6 +271,7 @@ class SendMessagePresenter @Inject constructor(
Timber.i("Draft cleared!")
}
- fun getMessageBackupContent(recipients: String) = if (recipients.isEmpty()) view?.getMessageBackupDialogString()
+ fun getMessageBackupContent(recipients: String) =
+ if (recipients.isEmpty()) view?.getMessageBackupDialogString()
else view?.getMessageBackupDialogStringWithRecipients(recipients)
}
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 f70a1bab7..57055a644 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,6 @@
package io.github.wulkanowy.ui.modules.message.tab
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.repositories.MessageRepository
@@ -9,17 +9,10 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.consumeAsFlow
-import kotlinx.coroutines.flow.debounce
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import me.xdrop.fuzzywuzzy.FuzzySearch
import timber.log.Timber
@@ -107,64 +100,75 @@ class MessageTabPresenter @Inject constructor(
) {
Timber.i("Loading $folder message data started")
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
messageRepository.getMessages(student, semester, folder, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- if (!it.data.isNullOrEmpty()) {
- view?.run {
- enableSwipe(true)
- showErrorView(false)
- showRefresh(true)
- showProgress(false)
- showContent(true)
- messages = it.data
- val filteredData = getFilteredData(
+ }
+ .logResourceStatus("load $folder message")
+ .onEach {
+ when (it) {
+ is Resource.Intermediate -> {
+ if (it.data.isNotEmpty()) {
+ view?.run {
+ enableSwipe(true)
+ showErrorView(false)
+ showRefresh(true)
+ showProgress(false)
+ showContent(true)
+ messages = it.data
+ val filteredData = getFilteredData(
+ lastSearchQuery,
+ onlyUnread,
+ onlyWithAttachments
+ )
+ val messageItems = filteredData.map { message ->
+ MessageTabDataItem.MessageItem(message)
+ }
+ val messageItemsWithHeader =
+ listOf(MessageTabDataItem.Header) + messageItems
+
+ updateData(
+ messageItemsWithHeader,
+ folder.id == MessageFolder.SENT.id
+ )
+ notifyParentDataLoaded()
+ }
+ }
+ }
+ is Resource.Success -> {
+ messages = it.data
+ updateData(
+ getFilteredData(
lastSearchQuery,
onlyUnread,
onlyWithAttachments
)
- val messageItems = filteredData.map { message ->
- MessageTabDataItem.MessageItem(message)
- }
- val messageItemsWithHeader =
- listOf(MessageTabDataItem.Header) + messageItems
-
- updateData(messageItemsWithHeader, folder.id == MessageFolder.SENT.id)
- notifyParentDataLoaded()
- }
+ )
+ analytics.logEvent(
+ "load_data",
+ "type" to "messages",
+ "items" to it.data.size,
+ "folder" to folder.name
+ )
}
- }
- Status.SUCCESS -> {
- Timber.i("Loading $folder message result: Success")
- messages = it.data!!
- updateData(getFilteredData(lastSearchQuery, onlyUnread, onlyWithAttachments))
- analytics.logEvent(
- "load_data",
- "type" to "messages",
- "items" to it.data.size,
- "folder" to folder.name
- )
- }
- Status.ERROR -> {
- Timber.i("Loading $folder message result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ else -> {}
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
- notifyParentDataLoaded()
+ .onResourceNotLoading {
+ view?.run {
+ showRefresh(false)
+ showProgress(false)
+ enableSwipe(true)
+ notifyParentDataLoaded()
+ }
}
- }.catch {
- errorHandler.dispatch(it)
- view?.notifyParentDataLoaded()
- }.launch()
+ .onResourceError(errorHandler::dispatch)
+ .catch {
+ errorHandler.dispatch(it)
+ view?.notifyParentDataLoaded()
+ }
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt
index 53049891a..36a720e53 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt
@@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.mobiledevice
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.repositories.MobileDeviceRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
@@ -8,10 +8,6 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
-import io.github.wulkanowy.utils.flowWithResourceIn
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -52,49 +48,39 @@ class MobileDevicePresenter @Inject constructor(
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading mobile devices data started")
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
mobileDeviceRepository.getDevices(student, semester, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- if (!it.data.isNullOrEmpty()) {
- view?.run {
- enableSwipe(true)
- showRefresh(true)
- showProgress(false)
- showContent(true)
- updateData(it.data)
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading mobile devices result: Success")
- view?.run {
- updateData(it.data!!)
- showContent(it.data.isNotEmpty())
- showEmpty(it.data.isEmpty())
- showErrorView(false)
- }
- analytics.logEvent(
- "load_data",
- "type" to "devices",
- "items" to it.data!!.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading mobile devices result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load mobile devices data")
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showContent(it.isNotEmpty())
+ showEmpty(it.isEmpty())
+ updateData(it)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "devices",
+ "items" to it.size
+ )
}
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showRefresh(false)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@@ -128,25 +114,19 @@ class MobileDevicePresenter @Inject constructor(
}
fun onUnregisterConfirmed(device: MobileDevice) {
- flowWithResource {
+ resourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
mobileDeviceRepository.unregisterDevice(student, semester, device)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Unregister device started")
- Status.SUCCESS -> {
- Timber.i("Unregister device result: Success")
- view?.run {
- showProgress(false)
- enableSwipe(true)
- }
- }
- Status.ERROR -> {
- Timber.i("Unregister device result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("unregister device")
+ .onResourceSuccess {
+ view?.run {
+ showProgress(false)
+ enableSwipe(true)
}
}
- }.launch("unregister")
+ .onResourceError(errorHandler::dispatch)
+ .launch("unregister")
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt
index 5e7110ee5..875b73ad7 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/token/MobileDeviceTokenPresenter.kt
@@ -1,15 +1,12 @@
package io.github.wulkanowy.ui.modules.mobiledevice.token
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.repositories.MobileDeviceRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -29,29 +26,29 @@ class MobileDeviceTokenPresenter @Inject constructor(
}
private fun loadData() {
- flowWithResource {
+ resourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
mobileDeviceRepository.getToken(student, semester)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Mobile device registration data started")
- Status.SUCCESS -> {
- Timber.i("Mobile device registration result: Success")
- view?.run {
- updateData(it.data!!)
- showContent()
- }
- analytics.logEvent("device_register", "symbol" to it.data!!.token.substring(0, 3))
- }
- Status.ERROR -> {
- Timber.i("Mobile device registration result: An exception occurred")
- view?.closeDialog()
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load mobile device registration")
+ .onResourceData {
+ view?.run {
+ updateData(it)
+ showContent()
}
}
- }.afterLoading {
- view?.hideLoading()
- }.launch()
+ .onResourceSuccess {
+ analytics.logEvent(
+ "device_register",
+ "symbol" to it.token.substring(0, 3)
+ )
+ }
+ .onResourceNotLoading { view?.hideLoading() }
+ .onResourceError {
+ view?.closeDialog()
+ errorHandler.dispatch(it)
+ }
+ .launch()
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt
index 10a391820..440565e11 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt
@@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.note
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.repositories.NoteRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
@@ -8,9 +8,6 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResource
-import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -51,51 +48,40 @@ class NotePresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
- Timber.i("Loading note data started")
-
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
noteRepository.getNotes(student, semester, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- if (!it.data.isNullOrEmpty()) {
- view?.run {
- enableSwipe(true)
- showRefresh(true)
- showProgress(false)
- showContent(true)
- updateData(it.data.sortedByDescending { item -> item.date })
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading note result: Success")
- view?.apply {
- updateData(it.data!!.sortedByDescending { item -> item.date })
- showEmpty(it.data.isEmpty())
- showErrorView(false)
- showContent(it.data.isNotEmpty())
- }
- analytics.logEvent(
- "load_data",
- "type" to "note",
- "items" to it.data!!.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading note result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load note data")
+ .mapResourceData { it.sortedByDescending { note -> note.date } }
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showContent(it.isNotEmpty())
+ showEmpty(it.isEmpty())
+ updateData(it)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "note",
+ "items" to it.size
+ )
}
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showRefresh(false)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@@ -122,14 +108,14 @@ class NotePresenter @Inject constructor(
}
private fun updateNote(note: Note) {
- flowWithResource { noteRepository.updateNote(note) }
+ resourceFlow { noteRepository.updateNote(note) }
.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Attempt to update note ${note.id}")
- Status.SUCCESS -> Timber.i("Update note result: Success")
- Status.ERROR -> {
+ when (it) {
+ is Resource.Loading -> Timber.i("Attempt to update note ${note.id}")
+ is Resource.Success -> Timber.i("Update note result: Success")
+ is Resource.Error -> {
Timber.i("Update note result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ errorHandler.dispatch(it.error)
}
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt
index ac8c273ea..262398b8a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/school/SchoolPresenter.kt
@@ -1,16 +1,13 @@
package io.github.wulkanowy.ui.modules.schoolandteachers.school
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.repositories.SchoolRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -65,46 +62,48 @@ class SchoolPresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
schoolRepository.getSchoolInfo(student, semester, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading school info started")
- Status.SUCCESS -> if (it.data != null) {
- Timber.i("Loading teachers result: Success")
+ }
+ .logResourceStatus("load school info")
+ .onResourceData {
+ if (it != null) {
view?.run {
- address = it.data.address.ifBlank { null }
- contact = it.data.contact.ifBlank { null }
- updateData(it.data)
+ address = it.address.ifBlank { null }
+ contact = it.contact.ifBlank { null }
+ updateData(it)
showContent(true)
showEmpty(false)
showErrorView(false)
}
- analytics.logEvent("load_item", "type" to "school")
} else view?.run {
Timber.i("Loading school result: No school info found")
showContent(!isViewEmpty)
showEmpty(isViewEmpty)
showErrorView(false)
}
- Status.ERROR -> {
- Timber.i("Loading school result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .onResourceSuccess {
+ if (it != null) {
+ analytics.logEvent("load_item", "type" to "school")
}
}
- }.afterLoading {
- view?.run {
- hideRefresh()
- showProgress(false)
- enableSwipe(true)
- notifyParentDataLoaded()
+ .onResourceNotLoading {
+ view?.run {
+ hideRefresh()
+ showProgress(false)
+ enableSwipe(true)
+ notifyParentDataLoaded()
+ }
}
- }.catch {
- errorHandler.dispatch(it)
- view?.notifyParentDataLoaded()
- }.launch()
+ .onResourceError(errorHandler::dispatch)
+ .catch {
+ errorHandler.dispatch(it)
+ view?.notifyParentDataLoaded()
+ }
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt
index bd46ff0b2..e2af05c92 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolandteachers/teacher/TeacherPresenter.kt
@@ -1,16 +1,13 @@
package io.github.wulkanowy.ui.modules.schoolandteachers.teacher
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TeacherRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -53,43 +50,41 @@ class TeacherPresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
teacherRepository.getTeachers(student, semester, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading teachers data started")
- Status.SUCCESS -> {
- Timber.i("Loading teachers result: Success")
- view?.run {
- updateData(it.data!!.filter { item -> item.name.isNotBlank() })
- showContent(it.data.isNotEmpty())
- showEmpty(it.data.isEmpty())
- showErrorView(false)
- }
- analytics.logEvent(
- "load_data",
- "type" to "teachers",
- "items" to it.data!!.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading teachers result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load teachers data")
+ .onResourceData {
+ view?.run {
+ updateData(it.filter { item -> item.name.isNotBlank() })
+ showContent(it.isNotEmpty())
+ showEmpty(it.isEmpty())
+ showErrorView(false)
}
}
- }.afterLoading {
- view?.run {
- hideRefresh()
- showProgress(false)
- enableSwipe(true)
- notifyParentDataLoaded()
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "teachers",
+ "items" to it.size
+ )
}
- }.catch {
- errorHandler.dispatch(it)
- view?.notifyParentDataLoaded()
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ hideRefresh()
+ showProgress(false)
+ enableSwipe(true)
+ notifyParentDataLoaded()
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .catch {
+ errorHandler.dispatch(it)
+ view?.notifyParentDataLoaded()
+ }
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt
index 62c93198d..f77a88335 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementPresenter.kt
@@ -1,15 +1,12 @@
package io.github.wulkanowy.ui.modules.schoolannouncement
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -52,50 +49,37 @@ class SchoolAnnouncementPresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
- Timber.i("Loading School announcement data started")
-
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
schoolAnnouncementRepository.getSchoolAnnouncements(student, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- if (!it.data.isNullOrEmpty()) {
- view?.run {
- enableSwipe(true)
- showRefresh(true)
- showErrorView(false)
- showProgress(false)
- showContent(true)
- updateData(it.data)
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading School announcement result: Success")
- view?.apply {
- updateData(it.data!!)
- showEmpty(it.data.isEmpty())
- showErrorView(false)
- showContent(it.data.isNotEmpty())
- }
- analytics.logEvent(
- "load_school_announcement",
- "items" to it.data!!.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading School announcement result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load school announcement")
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showContent(it.isNotEmpty())
+ showEmpty(it.isEmpty())
+ updateData(it)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_school_announcement",
+ "items" to it.size
+ )
}
- }.launch()
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceNotLoading {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showRefresh(false)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch("load_data")
}
private fun showErrorViewOnError(message: String, error: Throwable) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt
index 80798b11e..083b590b2 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoPresenter.kt
@@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.studentinfo
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.StudentInfo
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentInfoRepository
@@ -8,10 +8,7 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.getCurrentOrLast
-import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -72,51 +69,50 @@ class StudentInfoPresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
- flowWithResourceIn {
+ flatResourceFlow {
val semester = studentWithSemesters.semesters.getCurrentOrLast()
studentInfoRepository.getStudentInfo(
student = studentWithSemesters.student,
semester = semester,
forceRefresh = forceRefresh
)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading student info $infoType started")
- Status.SUCCESS -> {
- val isFamily = infoType == StudentInfoView.Type.FAMILY
- val isFirstGuardianEmpty = it.data?.firstGuardian == null
- val isSecondGuardianEmpty = it.data?.secondGuardian == null
-
- if (it.data != null && !(isFamily && isFirstGuardianEmpty && isSecondGuardianEmpty)) {
- Timber.i("Loading student info $infoType result: Success")
- showCorrectData(it.data)
- view?.run {
- showContent(true)
- showEmpty(false)
- showErrorView(false)
- }
- analytics.logEvent("load_item", "type" to "student_info")
- } else {
- Timber.i("Loading student info $infoType result: No student or family info found")
- view?.run {
- showContent(!isViewEmpty)
- showEmpty(isViewEmpty)
- showErrorView(false)
- }
+ }
+ .logResourceStatus("load student info $infoType")
+ .onResourceData {
+ val isFamily = infoType == StudentInfoView.Type.FAMILY
+ val isFirstGuardianEmpty = it?.firstGuardian == null
+ val isSecondGuardianEmpty = it?.secondGuardian == null
+ if (it != null && !(isFamily && isFirstGuardianEmpty && isSecondGuardianEmpty)) {
+ Timber.i("Loading student info $infoType result: Success")
+ showCorrectData(it)
+ view?.run {
+ showContent(true)
+ showEmpty(false)
+ showErrorView(false)
+ }
+ } else {
+ Timber.i("Loading student info $infoType result: No student or family info found")
+ view?.run {
+ showContent(!isViewEmpty)
+ showEmpty(isViewEmpty)
+ showErrorView(false)
}
}
- Status.ERROR -> {
- Timber.i("Loading student info $infoType result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .onResourceSuccess {
+ if (it != null) {
+ analytics.logEvent("load_item", "type" to "student_info")
}
}
- }.afterLoading {
- view?.run {
- hideRefresh()
- showProgress(false)
- enableSwipe(true)
+ .onResourceNotLoading {
+ view?.run {
+ hideRefresh()
+ showProgress(false)
+ enableSwipe(true)
+ }
}
- }.launch()
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun showCorrectData(studentInfo: StudentInfo) {
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 ec3ef7b02..dc6c89213 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
@@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.timetable
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.enums.TimetableMode
import io.github.wulkanowy.data.repositories.PreferencesRepository
@@ -123,57 +123,47 @@ class TimetablePresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
- Timber.i("Loading timetable data started")
-
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
timetableRepository.getTimetable(
- student, semester, currentDate, currentDate, forceRefresh
+ student = student,
+ semester = semester,
+ start = currentDate,
+ end = currentDate,
+ forceRefresh = forceRefresh,
+ timetableType = TimetableRepository.TimetableType.NORMAL
)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- if (!it.data?.lessons.isNullOrEmpty()) {
- view?.run {
- updateData(it.data!!.lessons)
- enableSwipe(true)
- showRefresh(true)
- showErrorView(false)
- showProgress(false)
- showContent(true)
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading timetable result: Success")
- view?.apply {
- updateData(it.data!!.lessons)
- showEmpty(it.data.lessons.isEmpty())
- setDayHeaderMessage(it.data.headers.singleOrNull { header ->
- header.date == currentDate
- }?.content)
- showErrorView(false)
- showContent(it.data.lessons.isNotEmpty())
- }
- analytics.logEvent(
- "load_data",
- "type" to "timetable",
- "items" to it.data!!.lessons.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading timetable result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ }
+ .logResourceStatus("load timetable data")
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showContent(it.lessons.isNotEmpty())
+ showEmpty(it.lessons.isEmpty())
+ updateData(it.lessons)
+ setDayHeaderMessage(it.headers.singleOrNull { header -> header.date == currentDate }?.content)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "timetable",
+ "items" to it.lessons.size
+ )
}
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showRefresh(false)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun updateData(lessons: List) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt
index 742a8d592..d0a01b38c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/additional/AdditionalLessonsPresenter.kt
@@ -1,23 +1,14 @@
package io.github.wulkanowy.ui.modules.timetable.additional
import android.annotation.SuppressLint
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.capitalise
-import io.github.wulkanowy.utils.flowWithResourceIn
-import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
-import io.github.wulkanowy.utils.isHolidays
-import io.github.wulkanowy.utils.nextOrSameSchoolDay
-import io.github.wulkanowy.utils.nextSchoolDay
-import io.github.wulkanowy.utils.previousSchoolDay
-import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
@@ -137,39 +128,44 @@ class AdditionalLessonsPresenter @Inject constructor(
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
currentDate = date
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
- timetableRepository.getTimetable(student, semester, date, date, forceRefresh, true)
- }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.i("Loading additional lessons data started")
- Status.SUCCESS -> {
- Timber.i("Loading additional lessons lessons result: Success")
- view?.apply {
- updateData(it.data!!.additional.sortedBy { item -> item.start })
- showEmpty(it.data.additional.isEmpty())
- showErrorView(false)
- showContent(it.data.additional.isNotEmpty())
- }
- analytics.logEvent(
- "load_data",
- "type" to "additional_lessons",
- "items" to it.data!!.additional.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading additional lessons result: An exception occurred")
- errorHandler.dispatch(it.error!!)
+ timetableRepository.getTimetable(
+ student = student,
+ semester = semester,
+ start = date,
+ end = date,
+ forceRefresh = forceRefresh,
+ refreshAdditional = true,
+ timetableType = TimetableRepository.TimetableType.ADDITIONAL
+ )
+ }
+ .logResourceStatus("load additional lessons")
+ .onResourceData {
+ view?.apply {
+ updateData(it.additional.sortedBy { item -> item.start })
+ showEmpty(it.additional.isEmpty())
+ showErrorView(false)
+ showContent(it.additional.isNotEmpty())
}
}
- }.afterLoading {
- view?.run {
- hideRefresh()
- showProgress(false)
- enableSwipe(true)
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "additional_lessons",
+ "items" to it.additional.size
+ )
}
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ hideRefresh()
+ showProgress(false)
+ enableSwipe(true)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt
index b75b42f8b..16c51fd2e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt
@@ -1,22 +1,13 @@
package io.github.wulkanowy.ui.modules.timetable.completed
import android.annotation.SuppressLint
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.repositories.CompletedLessonsRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
-import io.github.wulkanowy.utils.AnalyticsHelper
-import io.github.wulkanowy.utils.afterLoading
-import io.github.wulkanowy.utils.capitalise
-import io.github.wulkanowy.utils.flowWithResourceIn
-import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
-import io.github.wulkanowy.utils.isHolidays
-import io.github.wulkanowy.utils.nextOrSameSchoolDay
-import io.github.wulkanowy.utils.nextSchoolDay
-import io.github.wulkanowy.utils.previousSchoolDay
-import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
@@ -111,51 +102,46 @@ class CompletedLessonsPresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
- Timber.i("Loading completed lessons data started")
-
- flowWithResourceIn {
+ flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
- completedLessonsRepository.getCompletedLessons(student, semester, currentDate, currentDate, forceRefresh)
- }.onEach {
- when (it.status) {
- Status.LOADING -> {
- if (!it.data.isNullOrEmpty()) {
- view?.run {
- enableSwipe(true)
- showRefresh(true)
- showProgress(false)
- showContent(true)
- updateData(it.data.sortedBy { item -> item.number })
- }
- }
- }
- Status.SUCCESS -> {
- Timber.i("Loading completed lessons lessons result: Success")
- view?.apply {
- updateData(it.data!!.sortedBy { item -> item.number })
- showEmpty(it.data.isEmpty())
- showErrorView(false)
- showContent(it.data.isNotEmpty())
- }
- analytics.logEvent(
- "load_data",
- "type" to "completed_lessons",
- "items" to it.data!!.size
- )
- }
- Status.ERROR -> {
- Timber.i("Loading completed lessons result: An exception occurred")
- completedLessonsErrorHandler.dispatch(it.error!!)
+ completedLessonsRepository.getCompletedLessons(
+ student = student,
+ semester = semester,
+ start = currentDate,
+ end = currentDate,
+ forceRefresh = forceRefresh
+ )
+ }
+ .logResourceStatus("load completed lessons")
+ .mapResourceData { it.sortedBy { lesson -> lesson.number } }
+ .onResourceData {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showErrorView(false)
+ showContent(it.isNotEmpty())
+ showEmpty(it.isEmpty())
+ updateData(it)
}
}
- }.afterLoading {
- view?.run {
- showRefresh(false)
- showProgress(false)
- enableSwipe(true)
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "completed_lessons",
+ "items" to it.size
+ )
}
- }.launch()
+ .onResourceNotLoading {
+ view?.run {
+ enableSwipe(true)
+ showProgress(false)
+ showRefresh(false)
+ }
+ }
+ .onResourceError(errorHandler::dispatch)
+ .launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt
index 2a40c8e4a..dc2a7c6c7 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigurePresenter.kt
@@ -1,14 +1,14 @@
package io.github.wulkanowy.ui.modules.timetablewidget
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getThemeWidgetKey
-import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@@ -56,16 +56,15 @@ class TimetableWidgetConfigurePresenter @Inject constructor(
}
private fun loadData() {
- flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
- when (it.status) {
- Status.LOADING -> Timber.d("Timetable widget configure students data load")
- Status.SUCCESS -> {
+ resourceFlow { studentRepository.getSavedStudents(false) }.onEach {
+ when (it) {
+ is Resource.Loading -> Timber.d("Timetable widget configure students data load")
+ is Resource.Success -> {
val selectedStudentId = appWidgetId?.let { id ->
sharedPref.getLong(getStudentWidgetKey(id), 0)
} ?: -1
-
when {
- it.data!!.isEmpty() -> view?.openLoginView()
+ it.data.isEmpty() -> view?.openLoginView()
it.data.size == 1 && !isFromProvider -> {
selectedStudent = it.data.single().student
view?.showThemeDialog()
@@ -73,7 +72,7 @@ class TimetableWidgetConfigurePresenter @Inject constructor(
else -> view?.updateData(it.data, selectedStudentId)
}
}
- Status.ERROR -> errorHandler.dispatch(it.error!!)
+ is Resource.Error -> errorHandler.dispatch(it.error)
}
}.launch()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
index 18eefc5da..664086bca 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetFactory.kt
@@ -12,6 +12,7 @@ import android.widget.AdapterView.INVALID_POSITION
import android.widget.RemoteViews
import android.widget.RemoteViewsService
import io.github.wulkanowy.R
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.enums.TimetableMode
@@ -19,12 +20,12 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getCurrentThemeWidgetKey
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getDateWidgetKey
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getTodayLastLessonEndDateTimeWidgetKey
import io.github.wulkanowy.utils.getCompatColor
-import io.github.wulkanowy.utils.toFirstResult
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.runBlocking
import timber.log.Timber
@@ -118,7 +119,7 @@ class TimetableWidgetFactory(
val semester = semesterRepository.getCurrentSemester(student)
timetableRepository.getTimetable(student, semester, date, date, false)
- .toFirstResult().data?.lessons.orEmpty()
+ .toFirstResult().dataOrNull?.lessons.orEmpty()
.sortedWith(compareBy({ it.number }, { !it.isStudentPlan }))
.filter {
if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
diff --git a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt
deleted file mode 100644
index 5dd289677..000000000
--- a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-package io.github.wulkanowy.utils
-
-import io.github.wulkanowy.data.Resource
-import io.github.wulkanowy.data.Status
-import kotlinx.coroutines.FlowPreview
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.emitAll
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.takeWhile
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
-
-inline fun networkBoundResource(
- mutex: Mutex = Mutex(),
- showSavedOnLoading: Boolean = true,
- crossinline query: () -> Flow,
- crossinline fetch: suspend (ResultType) -> RequestType,
- crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit,
- crossinline onFetchFailed: (Throwable) -> Unit = { },
- crossinline shouldFetch: (ResultType) -> Boolean = { true },
- crossinline filterResult: (ResultType) -> ResultType = { it }
-) = flow {
- emit(Resource.loading())
-
- val data = query().first()
- emitAll(if (shouldFetch(data)) {
- if (showSavedOnLoading) emit(Resource.loading(filterResult(data)))
-
- try {
- val newData = fetch(data)
- mutex.withLock { saveFetchResult(query().first(), newData) }
- query().map { Resource.success(filterResult(it)) }
- } catch (throwable: Throwable) {
- onFetchFailed(throwable)
- query().map { Resource.error(throwable, filterResult(it)) }
- }
- } else {
- query().map { Resource.success(filterResult(it)) }
- })
-}
-
-@JvmName("networkBoundResourceWithMap")
-inline fun networkBoundResource(
- mutex: Mutex = Mutex(),
- showSavedOnLoading: Boolean = true,
- crossinline query: () -> Flow,
- crossinline fetch: suspend (ResultType) -> RequestType,
- crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit,
- crossinline onFetchFailed: (Throwable) -> Unit = { },
- crossinline shouldFetch: (ResultType) -> Boolean = { true },
- crossinline mapResult: (ResultType) -> T
-) = flow {
- emit(Resource.loading())
-
- val data = query().first()
- emitAll(if (shouldFetch(data)) {
- if (showSavedOnLoading) emit(Resource.loading(mapResult(data)))
-
- try {
- val newData = fetch(data)
- mutex.withLock { saveFetchResult(query().first(), newData) }
- query().map { Resource.success(mapResult(it)) }
- } catch (throwable: Throwable) {
- onFetchFailed(throwable)
- query().map { Resource.error(throwable, mapResult(it)) }
- }
- } else {
- query().map { Resource.success(mapResult(it)) }
- })
-}
-
-fun flowWithResource(block: suspend () -> T) = flow {
- emit(Resource.loading())
- emit(Resource.success(block()))
-}.catch { emit(Resource.error(it)) }
-
-@OptIn(FlowPreview::class)
-fun flowWithResourceIn(block: suspend () -> Flow>) = flow {
- emit(Resource.loading())
- emitAll(block().filter { it.status != Status.LOADING || (it.status == Status.LOADING && it.data != null) })
-}.catch { emit(Resource.error(it)) }
-
-fun Flow>.afterLoading(callback: () -> Unit) = onEach {
- if (it.status != Status.LOADING) callback()
-}
-
-suspend fun Flow>.toFirstResult() = filter { it.status != Status.LOADING }.first()
-
-suspend fun Flow>.waitForResult() =
- takeWhile { it.status == Status.LOADING }.collect()
diff --git a/app/src/test/java/io/github/wulkanowy/utils/FlowUtilsKtTest.kt b/app/src/test/java/io/github/wulkanowy/data/ResourceTest.kt
similarity index 96%
rename from app/src/test/java/io/github/wulkanowy/utils/FlowUtilsKtTest.kt
rename to app/src/test/java/io/github/wulkanowy/data/ResourceTest.kt
index 57045a29d..ea846a57b 100644
--- a/app/src/test/java/io/github/wulkanowy/utils/FlowUtilsKtTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/ResourceTest.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.utils
+package io.github.wulkanowy.data
import io.mockk.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -13,7 +13,7 @@ import org.junit.Test
import kotlin.test.assertEquals
@OptIn(ExperimentalCoroutinesApi::class)
-class FlowUtilsKtTest {
+class ResourceTest {
private val testScope = TestScope(UnconfinedTestDispatcher())
@@ -41,6 +41,7 @@ class FlowUtilsKtTest {
// first
networkBoundResource(
+ isResultEmpty = { false },
showSavedOnLoading = false,
query = { repo.query() },
fetch = {
@@ -55,6 +56,7 @@ class FlowUtilsKtTest {
// second
networkBoundResource(
+ isResultEmpty = { false },
showSavedOnLoading = false,
query = { repo.query() },
fetch = {
@@ -120,6 +122,7 @@ class FlowUtilsKtTest {
// first
networkBoundResource(
+ isResultEmpty = { false },
mutex = saveResultMutex,
showSavedOnLoading = false,
query = { repo.query() },
@@ -138,6 +141,7 @@ class FlowUtilsKtTest {
// second
networkBoundResource(
+ isResultEmpty = { false },
mutex = saveResultMutex,
showSavedOnLoading = false,
query = { repo.query() },
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
index f3c7fba77..7d22f7265 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
@@ -1,20 +1,17 @@
package io.github.wulkanowy.data.repositories
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.dao.AttendanceDao
+import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.toFirstResult
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerify
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
-import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
@@ -73,8 +70,8 @@ class AttendanceRepositoryTest {
val res = runBlocking { attendanceRepository.getAttendance(student, semester, startDate, endDate, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(2, res.data?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getAttendance(startDate, endDate, 1) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
@@ -97,8 +94,8 @@ class AttendanceRepositoryTest {
val res = runBlocking { attendanceRepository.getAttendance(student, semester, startDate, endDate, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(2, res.data?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getAttendance(startDate, endDate, 1) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify {
@@ -125,8 +122,8 @@ class AttendanceRepositoryTest {
val res = runBlocking { attendanceRepository.getAttendance(student, semester, startDate, endDate, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(1, res.data?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getAttendance(startDate, endDate, 1) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt
index fa54522a8..c28ea304b 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt
@@ -1,20 +1,17 @@
package io.github.wulkanowy.data.repositories
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
+import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.toFirstResult
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerify
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
-import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
@@ -73,8 +70,8 @@ class CompletedLessonsRepositoryTest {
val res = runBlocking { completedLessonRepository.getCompletedLessons(student, semester, startDate, endDate, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(2, res.data?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getCompletedLessons(startDate, endDate) }
coVerify { completedLessonDb.loadAll(1, 1, startDate, endDate) }
coVerify { completedLessonDb.insertAll(match { it.isEmpty() }) }
@@ -97,8 +94,8 @@ class CompletedLessonsRepositoryTest {
val res = runBlocking { completedLessonRepository.getCompletedLessons(student, semester, startDate, endDate, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(2, res.data?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getCompletedLessons(startDate, endDate) }
coVerify { completedLessonDb.loadAll(1, 1, startDate, endDate) }
coVerify {
@@ -125,8 +122,8 @@ class CompletedLessonsRepositoryTest {
val res = runBlocking { completedLessonRepository.getCompletedLessons(student, semester, startDate, endDate, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(1, res.data?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getCompletedLessons(startDate, endDate) }
coVerify { completedLessonDb.loadAll(1, 1, startDate, endDate) }
coVerify { completedLessonDb.insertAll(match { it.isEmpty() }) }
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt
index 8bf4deee3..e3790662e 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt
@@ -1,20 +1,17 @@
package io.github.wulkanowy.data.repositories
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.dao.ExamDao
+import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.toFirstResult
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerify
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
-import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
@@ -74,8 +71,8 @@ class ExamRemoteTest {
val res = runBlocking { examRepository.getExams(student, semester, startDate, endDate, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(2, res.data?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getExams(startDate, realEndDate, 1) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
coVerify { examDb.insertAll(match { it.isEmpty() }) }
@@ -98,8 +95,8 @@ class ExamRemoteTest {
val res = runBlocking { examRepository.getExams(student, semester, startDate, endDate, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(2, res.data?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getExams(startDate, realEndDate, 1) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
coVerify {
@@ -126,8 +123,8 @@ class ExamRemoteTest {
val res = runBlocking { examRepository.getExams(student, semester, startDate, endDate, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(1, res.data?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getExams(startDate, realEndDate, 1) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
coVerify { examDb.insertAll(match { it.isEmpty() }) }
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
index f7968bc41..e8d0b6c8f 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
@@ -1,13 +1,15 @@
package io.github.wulkanowy.data.repositories
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.dao.GradeDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
+import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.toFirstResult
import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
@@ -88,8 +90,8 @@ class GradeRepositoryTest {
}
// verify
- assertEquals(null, res.error)
- assertEquals(4, res.data?.first?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(4, res.dataOrNull?.first?.size)
coVerify {
gradeDb.insertAll(withArg {
assertEquals(4, it.size)
@@ -142,8 +144,8 @@ class GradeRepositoryTest {
val res = runBlocking { gradeRepository.getGrades(student, semester, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(4, res.data?.first?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(4, res.dataOrNull?.first?.size)
coVerify {
gradeDb.insertAll(withArg {
assertEquals(3, it.size)
@@ -184,8 +186,8 @@ class GradeRepositoryTest {
val res = runBlocking { gradeRepository.getGrades(student, semester, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(2, res.data?.first?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(2, res.dataOrNull?.first?.size)
coVerify { gradeDb.insertAll(match { it.isEmpty() }) }
coVerify { gradeDb.deleteAll(match { it.size == 1 }) } // ... here
}
@@ -214,8 +216,8 @@ class GradeRepositoryTest {
val res = runBlocking { gradeRepository.getGrades(student, semester, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(3, res.data?.first?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(3, res.dataOrNull?.first?.size)
coVerify { gradeDb.insertAll(match { it.size == 1 }) } // ... here
coVerify { gradeDb.deleteAll(match { it.isEmpty() }) }
}
@@ -240,8 +242,8 @@ class GradeRepositoryTest {
val res = runBlocking { gradeRepository.getGrades(student, semester, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(3, res.data?.first?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(3, res.dataOrNull?.first?.size)
}
@Test
@@ -263,8 +265,8 @@ class GradeRepositoryTest {
val res = runBlocking { gradeRepository.getGrades(student, semester, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(0, res.data?.first?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(0, res.dataOrNull?.first?.size)
}
private fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String) =
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt
index 6221b6989..8e2f7c6ef 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt
@@ -1,16 +1,18 @@
package io.github.wulkanowy.data.repositories
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
+import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.GradeStatisticsItem
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.toFirstResult
import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
@@ -81,11 +83,11 @@ class GradeStatisticsRepositoryTest {
forceRefresh = true,
).toFirstResult()
}
- val items = res.data.orEmpty()
+ val items = res.dataOrNull.orEmpty()
// verify
- assertEquals(null, res.error)
- assertEquals(2 + 1, res.data?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(2 + 1, res.dataOrNull?.size)
assertEquals("", items[0].partial?.studentAverage)
assertEquals("", items[1].partial?.studentAverage)
assertEquals("", items[2].partial?.studentAverage)
@@ -119,11 +121,11 @@ class GradeStatisticsRepositoryTest {
forceRefresh = true,
).toFirstResult()
}
- val items = res.data.orEmpty()
+ val items = res.dataOrNull.orEmpty()
// verify
- assertEquals(null, res.error)
- assertEquals(2 + 1, res.data?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(2 + 1, res.dataOrNull?.size)
assertEquals("3,00", items[0].partial?.studentAverage)
assertEquals("1.0", items[1].partial?.studentAverage)
assertEquals("5.0", items[2].partial?.studentAverage)
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt
index a89aad35c..3225c3bd2 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt
@@ -1,17 +1,15 @@
package io.github.wulkanowy.data.repositories
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
+import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.mappers.mapToEntity
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.toFirstResult
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerify
+import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
-import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
@@ -58,8 +56,8 @@ class LuckyNumberRemoteTest {
val res = runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(luckyNumber.number, res.data?.luckyNumber)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(luckyNumber.number, res.dataOrNull?.luckyNumber)
coVerify { sdk.getLuckyNumber(student.schoolShortName) }
coVerify { luckyNumberDb.load(1, date) }
coVerify(exactly = 0) { luckyNumberDb.insertAll(any()) }
@@ -82,8 +80,8 @@ class LuckyNumberRemoteTest {
val res = runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(luckyNumber.number, res.data?.luckyNumber)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(luckyNumber.number, res.dataOrNull?.luckyNumber)
coVerify { sdk.getLuckyNumber(student.schoolShortName) }
coVerify { luckyNumberDb.load(1, date) }
coVerify {
@@ -112,8 +110,8 @@ class LuckyNumberRemoteTest {
val res = runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(luckyNumber.number, res.data?.luckyNumber)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(luckyNumber.number, res.dataOrNull?.luckyNumber)
coVerify { sdk.getLuckyNumber(student.schoolShortName) }
coVerify { luckyNumberDb.load(1, date) }
coVerify {
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
index f21fc1780..2a5d2e2b4 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
@@ -1,13 +1,15 @@
package io.github.wulkanowy.data.repositories
import android.content.Context
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
import io.github.wulkanowy.data.db.dao.MessagesDao
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.enums.MessageFolder
+import io.github.wulkanowy.data.errorOrNull
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
@@ -15,7 +17,8 @@ import io.github.wulkanowy.sdk.pojo.Folder
import io.github.wulkanowy.sdk.pojo.MessageDetails
import io.github.wulkanowy.sdk.pojo.Sender
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.toFirstResult
+import io.github.wulkanowy.utils.Status
+import io.github.wulkanowy.utils.status
import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
@@ -102,7 +105,7 @@ class MessageRepositoryTest {
folder = MessageFolder.RECEIVED,
forceRefresh = true,
notify = true, // all new messages will be marked as not notified
- ).toFirstResult().data.orEmpty()
+ ).toFirstResult().dataOrNull.orEmpty()
coVerify(exactly = 1) { messageDb.deleteAll(emptyList()) }
coVerify(exactly = 1) { messageDb.insertAll(emptyList()) }
@@ -133,7 +136,7 @@ class MessageRepositoryTest {
folder = MessageFolder.RECEIVED,
forceRefresh = true,
notify = false,
- ).toFirstResult().data.orEmpty()
+ ).toFirstResult().dataOrNull.orEmpty()
coVerify(exactly = 1) { messageDb.deleteAll(withArg { checkEquals(emptyList()) }) }
coVerify {
@@ -165,9 +168,9 @@ class MessageRepositoryTest {
val res = runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
- assertEquals(null, res.error)
+ assertEquals(null, res.errorOrNull)
assertEquals(Status.SUCCESS, res.status)
- assertEquals("Test", res.data!!.message.content)
+ assertEquals("Test", res.dataOrNull!!.message.content)
}
@Test
@@ -197,9 +200,9 @@ class MessageRepositoryTest {
val res = runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
- assertEquals(null, res.error)
+ assertEquals(null, res.errorOrNull)
assertEquals(Status.SUCCESS, res.status)
- assertEquals("Test", res.data!!.message.content)
+ assertEquals("Test", res.dataOrNull!!.message.content)
coVerify { messageDb.updateAll(listOf(testMessageWithContent)) }
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
index c5a7756ff..b9a958d43 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
@@ -1,21 +1,18 @@
package io.github.wulkanowy.data.repositories
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
+import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Device
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.toFirstResult
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerify
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
-import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert
@@ -69,8 +66,8 @@ class MobileDeviceRepositoryTest {
val res = runBlocking { mobileDeviceRepository.getDevices(student, semester, true).toFirstResult() }
// verify
- Assert.assertEquals(null, res.error)
- Assert.assertEquals(2, res.data?.size)
+ Assert.assertEquals(null, res.errorOrNull)
+ Assert.assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getRegisteredDevices() }
coVerify { mobileDeviceDb.loadAll(1) }
coVerify { mobileDeviceDb.insertAll(match { it.isEmpty() }) }
@@ -93,8 +90,8 @@ class MobileDeviceRepositoryTest {
val res = runBlocking { mobileDeviceRepository.getDevices(student, semester, true).toFirstResult() }
// verify
- Assert.assertEquals(null, res.error)
- Assert.assertEquals(2, res.data?.size)
+ Assert.assertEquals(null, res.errorOrNull)
+ Assert.assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getRegisteredDevices() }
coVerify { mobileDeviceDb.loadAll(1) }
coVerify {
@@ -121,8 +118,8 @@ class MobileDeviceRepositoryTest {
val res = runBlocking { mobileDeviceRepository.getDevices(student, semester, true).toFirstResult() }
// verify
- Assert.assertEquals(null, res.error)
- Assert.assertEquals(1, res.data?.size)
+ Assert.assertEquals(null, res.errorOrNull)
+ Assert.assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getRegisteredDevices() }
coVerify { mobileDeviceDb.loadAll(1) }
coVerify { mobileDeviceDb.insertAll(match { it.isEmpty() }) }
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
index adb4f33a1..e56aaa5d0 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
@@ -1,24 +1,21 @@
package io.github.wulkanowy.data.repositories
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
+import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.TimetableFull
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.toFirstResult
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerify
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
-import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
@@ -96,8 +93,8 @@ class TimetableRepositoryTest {
val res = runBlocking { timetableRepository.getTimetable(student, semester, startDate, endDate, true).toFirstResult() }
// verify
- assertEquals(null, res.error)
- assertEquals(2, res.data?.lessons?.size)
+ assertEquals(null, res.errorOrNull)
+ assertEquals(2, res.dataOrNull!!.lessons.size)
coVerify { sdk.getTimetableFull(startDate, endDate) }
coVerify { timetableDb.loadAll(1, 1, startDate, endDate) }
coVerify { timetableDb.insertAll(match { it.isEmpty() }) }
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 5e8c4c119..a6ecdc26b 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
@@ -1,16 +1,18 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.Resource
-import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
+import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.flowWithResource
+import io.github.wulkanowy.utils.Status
+import io.github.wulkanowy.utils.status
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.every
@@ -29,7 +31,7 @@ import java.time.LocalDate.of
class GradeAverageProviderTest {
- private suspend fun Flow>.getResult() = toList()[1].data!!
+ private suspend fun Flow>.getResult() = toList()[1].dataOrNull!!
@MockK
lateinit var preferencesRepository: PreferencesRepository
@@ -144,7 +146,13 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageForceCalc } returns false
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { noWeightGrades to noWeightGradesSummary }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { noWeightGrades to noWeightGradesSummary }
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
@@ -156,7 +164,13 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageForceCalc } returns false
every { preferencesRepository.isOptionalArithmeticAverage } returns true
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { noWeightGrades to noWeightGradesArithmeticSummary }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { noWeightGrades to noWeightGradesArithmeticSummary }
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
@@ -170,27 +184,27 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flow {
- emit(Resource.loading())
- emit(Resource.loading(secondGradeWithModifier to secondSummariesWithModifier))
- emit(Resource.success(secondGradeWithModifier to secondSummariesWithModifier))
+ emit(Resource.Loading())
+ emit(Resource.Intermediate(secondGradeWithModifier to secondSummariesWithModifier))
+ emit(Resource.Success(secondGradeWithModifier to secondSummariesWithModifier))
}
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).toList() }
with(items[0]) {
assertEquals(Status.LOADING, status)
- assertEquals(null, data)
+ assertEquals(null, dataOrNull)
}
with(items[1]) {
assertEquals(Status.LOADING, status)
- assertEquals(1, data!!.size)
+ assertEquals(1, dataOrNull?.size)
}
with(items[2]) {
assertEquals(Status.SUCCESS, status)
- assertEquals(1, data!!.size)
+ assertEquals(1, dataOrNull?.size)
}
- assertEquals(3.5, items[1].data?.single { it.subject == "Język polski" }!!.average, .0) // from details and after set custom plus/minus
+ assertEquals(3.5, items[1].dataOrNull?.single { it.subject == "Język polski" }?.average ?: 0.0, .0) // from details and after set custom plus/minus
}
@Test
@@ -201,27 +215,27 @@ class GradeAverageProviderTest {
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[2], false) } returns flow {
- emit(Resource.loading())
+ emit(Resource.Loading())
delay(1000)
- emit(Resource.success(secondGradeWithModifier to secondSummariesWithModifier))
+ emit(Resource.Success(secondGradeWithModifier to secondSummariesWithModifier))
}
coEvery { gradeRepository.getGrades(student, semesters[1], false) } returns flow {
- emit(Resource.loading())
- emit(Resource.success(secondGradeWithModifier to secondSummariesWithModifier))
+ emit(Resource.Loading())
+ emit(Resource.Success(secondGradeWithModifier to secondSummariesWithModifier))
}
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, false).toList() }
with(items[0]) {
assertEquals(Status.LOADING, status)
- assertEquals(null, data)
+ assertEquals(null, dataOrNull)
}
with(items[1]) {
assertEquals(Status.SUCCESS, status)
- assertEquals(1, data!!.size)
+ assertEquals(1, dataOrNull?.size)
}
- assertEquals(3.5, items[1].data?.single { it.subject == "Język polski" }!!.average, .0) // from details and after set custom plus/minus
+ assertEquals(3.5, items[1].dataOrNull?.single { it.subject == "Język polski" }?.average ?: 0.0, .0) // from details and after set custom plus/minus
}
@Test
@@ -230,12 +244,26 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
- coEvery { gradeRepository.getGrades(student, semesters[1], false) } returns flowWithResource { secondGradeWithModifier to secondSummariesWithModifier }
- coEvery { gradeRepository.getGrades(student, semesters[2], false) } returns flowWithResource {
- listOf(getGrade(semesters[2].semesterId, "Język polski", .0, .0, .0)) to listOf(getSummary(semesters[2].semesterId, "Język polski", 2.5))
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ false
+ )
+ } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
+ coEvery { gradeRepository.getGrades(student, semesters[2], false) } returns resourceFlow {
+ listOf(getGrade(semesters[2].semesterId, "Język polski", .0, .0, .0)) to listOf(
+ getSummary(semesters[2].semesterId, "Język polski", 2.5)
+ )
}
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, false).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ false
+ ).getResult()
+ }
assertEquals(3.5, items.single { it.subject == "Język polski" }.average, .0)
}
@@ -246,10 +274,28 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
- coEvery { gradeRepository.getGrades(student, semesters[1], false) } returns flowWithResource { secondGradeWithModifier to secondSummariesWithModifier }
- coEvery { gradeRepository.getGrades(student, semesters[2], false) } returns flowWithResource { emptyList() to listOf(getSummary(24, "Język polski", .0))}
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ false
+ )
+ } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ false
+ )
+ } returns resourceFlow { emptyList() to listOf(getSummary(24, "Język polski", .0)) }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, false).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ false
+ ).getResult()
+ }
assertEquals(3.49, items.single { it.subject == "Język polski" }.average, .0)
}
@@ -260,10 +306,28 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource { emptyList() to emptyList() }
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { emptyList() to emptyList() }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ true
+ )
+ } returns resourceFlow { emptyList() to emptyList() }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { emptyList() to emptyList() }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
assertEquals(0, items.size)
}
@@ -274,7 +338,13 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
coEvery { semesterRepository.getSemesters(student) } returns semesters
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { secondGradeWithModifier to secondSummariesWithModifier }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
@@ -292,7 +362,13 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
coEvery { semesterRepository.getSemesters(student) } returns semesters
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { secondGradeWithModifier to secondSummariesWithModifier }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
@@ -310,7 +386,13 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
coEvery { semesterRepository.getSemesters(student) } returns semesters
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { secondGradeWithModifier to secondSummariesWithModifier }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
@@ -328,7 +410,13 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
coEvery { semesterRepository.getSemesters(student) } returns semesters
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { secondGradeWithModifier to secondSummariesWithModifier }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { secondGradeWithModifier to secondSummariesWithModifier }
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
@@ -340,7 +428,13 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageForceCalc } returns false
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { secondGrades to secondSummaries }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { secondGrades to secondSummaries }
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
@@ -354,7 +448,13 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageForceCalc } returns true
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ONE_SEMESTER
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { secondGrades to secondSummaries }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { secondGrades to secondSummaries }
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
@@ -368,7 +468,13 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageForceCalc } returns true
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource { firstGrades to firstSummaries }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ true
+ )
+ } returns resourceFlow { firstGrades to firstSummaries }
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[1].semesterId, true).getResult() }
@@ -384,29 +490,29 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flow {
- emit(Resource.loading())
- emit(Resource.loading(firstGrades to firstSummaries))
- emit(Resource.success(firstGrades to firstSummaries))
+ emit(Resource.Loading())
+ emit(Resource.Intermediate(firstGrades to firstSummaries))
+ emit(Resource.Success(firstGrades to firstSummaries))
}
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[1].semesterId, true).toList() }
with(items[0]) {
assertEquals(Status.LOADING, status)
- assertEquals(null, data)
+ assertEquals(null, dataOrNull)
}
with(items[1]) {
assertEquals(Status.LOADING, status)
- assertEquals(2, data!!.size)
+ assertEquals(2, dataOrNull?.size)
}
with(items[2]) {
assertEquals(Status.SUCCESS, status)
- assertEquals(2, data!!.size)
+ assertEquals(2, dataOrNull?.size)
}
- assertEquals(2, items[2].data!!.size)
- assertEquals(3.5, items[2].data!!.single { it.subject == "Matematyka" }.average, .0) // (from summary): 3,5
- assertEquals(3.5, items[2].data!!.single { it.subject == "Fizyka" }.average, .0) // (from summary): 3,5
+ assertEquals(2, items[2].dataOrNull?.size)
+ assertEquals(3.5, items[2].dataOrNull?.single { it.subject == "Matematyka" }?.average ?: 0.0, .0) // (from summary): 3,5
+ assertEquals(3.5, items[2].dataOrNull?.single { it.subject == "Fizyka" }?.average ?: 0.0, .0) // (from summary): 3,5
}
@Test
@@ -414,13 +520,13 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
every { preferencesRepository.gradeAverageForceCalc } returns false
every { preferencesRepository.isOptionalArithmeticAverage } returns false
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
firstGrades to listOf(
getSummary(22, "Matematyka", 3.0),
getSummary(22, "Fizyka", 3.5)
)
}
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
secondGrades to listOf(
getSummary(22, "Matematyka", 3.5),
getSummary(22, "Fizyka", 4.0)
@@ -440,46 +546,62 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageForceCalc } returns false
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flow {
- emit(Resource.loading())
- emit(Resource.loading(firstGrades to listOf(
- getSummary(22, "Matematyka", 3.0),
- getSummary(22, "Fizyka", 3.5)
- )))
- emit(Resource.success(firstGrades to listOf(
- getSummary(22, "Matematyka", 3.0),
- getSummary(22, "Fizyka", 3.5)
- )))
+ emit(Resource.Loading())
+ emit(
+ Resource.Intermediate(
+ firstGrades to listOf(
+ getSummary(22, "Matematyka", 3.0),
+ getSummary(22, "Fizyka", 3.5)
+ )
+ )
+ )
+ emit(
+ Resource.Success(
+ firstGrades to listOf(
+ getSummary(22, "Matematyka", 3.0),
+ getSummary(22, "Fizyka", 3.5)
+ )
+ )
+ )
}
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flow {
- emit(Resource.loading())
- emit(Resource.loading(secondGrades to listOf(
- getSummary(22, "Matematyka", 3.5),
- getSummary(22, "Fizyka", 4.0)
- )))
- emit(Resource.success(secondGrades to listOf(
- getSummary(22, "Matematyka", 3.5),
- getSummary(22, "Fizyka", 4.0)
- )))
+ emit(Resource.Loading())
+ emit(
+ Resource.Intermediate(
+ secondGrades to listOf(
+ getSummary(22, "Matematyka", 3.5),
+ getSummary(22, "Fizyka", 4.0)
+ )
+ )
+ )
+ emit(
+ Resource.Success(
+ secondGrades to listOf(
+ getSummary(22, "Matematyka", 3.5),
+ getSummary(22, "Fizyka", 4.0)
+ )
+ )
+ )
}
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).toList() }
with(items[0]) {
assertEquals(Status.LOADING, status)
- assertEquals(null, data)
+ assertEquals(null, dataOrNull)
}
with(items[1]) {
assertEquals(Status.LOADING, status)
- assertEquals(2, data!!.size)
+ assertEquals(2, dataOrNull?.size)
}
with(items[2]) {
assertEquals(Status.SUCCESS, status)
- assertEquals(2, data!!.size)
+ assertEquals(2, dataOrNull?.size)
}
- assertEquals(2, items[2].data!!.size)
- assertEquals(3.25, items[2].data!!.single { it.subject == "Matematyka" }.average, .0) // (from summaries ↑): 3,0 + 3,5 → 3,25
- assertEquals(3.75, items[2].data!!.single { it.subject == "Fizyka" }.average, .0) // (from summaries ↑): 3,5 + 4,0 → 3,75
+ assertEquals(2, items[2].dataOrNull?.size)
+ assertEquals(3.25, items[2].dataOrNull?.single { it.subject == "Matematyka" }?.average ?: 0.0, .0) // (from summaries ↑): 3,0 + 3,5 → 3,25
+ assertEquals(3.75, items[2].dataOrNull?.single { it.subject == "Fizyka" }?.average ?: 0.0, .0) // (from summaries ↑): 3,5 + 4,0 → 3,75
}
@Test
@@ -487,8 +609,14 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageForceCalc } returns true
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource { firstGrades to firstSummaries }
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource {
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ true
+ )
+ } returns resourceFlow { firstGrades to firstSummaries }
+ coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
secondGrades to listOf(
getSummary(22, "Matematyka", 1.1),
getSummary(22, "Fizyka", 7.26)
@@ -527,7 +655,7 @@ class GradeAverageProviderTest {
semesters[1],
true
)
- } returns flowWithResource {
+ } returns resourceFlow {
firstGrades to listOf(
getSummary(22, "Matematyka", .0),
getSummary(22, "Fizyka", .0)
@@ -539,7 +667,7 @@ class GradeAverageProviderTest {
semesters[2],
true
)
- } returns flowWithResource {
+ } returns resourceFlow {
secondGrades to listOf(
getSummary(22, "Matematyka", .0),
getSummary(22, "Fizyka", .0)
@@ -566,40 +694,48 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
coEvery { semesterRepository.getSemesters(student) } returns semesters
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flow {
- emit(Resource.loading())
- emit(Resource.loading(firstGrades to firstSummaries))
- emit(Resource.success(firstGrades to firstSummaries))
+ emit(Resource.Loading())
+ emit(Resource.Intermediate(firstGrades to firstSummaries))
+ emit(Resource.Success(firstGrades to firstSummaries))
}
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flow {
- emit(Resource.loading())
- emit(Resource.loading(secondGrades to listOf(
- getSummary(22, "Matematyka", 1.1),
- getSummary(22, "Fizyka", 7.26)
- )))
- emit(Resource.success(secondGrades to listOf(
- getSummary(22, "Matematyka", 1.1),
- getSummary(22, "Fizyka", 7.26)
- )))
+ emit(Resource.Loading())
+ emit(
+ Resource.Intermediate(
+ secondGrades to listOf(
+ getSummary(22, "Matematyka", 1.1),
+ getSummary(22, "Fizyka", 7.26)
+ )
+ )
+ )
+ emit(
+ Resource.Success(
+ secondGrades to listOf(
+ getSummary(22, "Matematyka", 1.1),
+ getSummary(22, "Fizyka", 7.26)
+ )
+ )
+ )
}
val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).toList() }
with(items[0]) {
assertEquals(Status.LOADING, status)
- assertEquals(null, data)
+ assertEquals(null, dataOrNull)
}
with(items[1]) {
assertEquals(Status.LOADING, status)
- assertEquals(2, data!!.size)
+ assertEquals(2, dataOrNull?.size)
}
with(items[2]) {
assertEquals(Status.SUCCESS, status)
- assertEquals(2, data!!.size)
+ assertEquals(2, dataOrNull?.size)
}
- assertEquals(2, items[2].data!!.size)
- assertEquals(3.0, items[2].data!!.single { it.subject == "Matematyka" }.average, .0) // (from details): 3,5 + 2,5 → 3,0
- assertEquals(3.25, items[2].data!!.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25
+ assertEquals(2, items[2].dataOrNull?.size)
+ assertEquals(3.0, items[2].dataOrNull?.single { it.subject == "Matematyka" }?.average ?: 0.0, .0) // (from details): 3,5 + 2,5 → 3,0
+ assertEquals(3.25, items[2].dataOrNull?.single { it.subject == "Fizyka" }?.average ?: 0.0, .0) // (from details): 3,5 + 3,0 → 3,25
}
@Test
@@ -614,14 +750,14 @@ class GradeAverageProviderTest {
semesters[1],
true
)
- } returns flowWithResource { firstGrades to emptyList() }
+ } returns resourceFlow { firstGrades to emptyList() }
coEvery {
gradeRepository.getGrades(
student,
semesters[2],
true
)
- } returns flowWithResource { secondGrades to emptyList() }
+ } returns resourceFlow { secondGrades to emptyList() }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
@@ -646,14 +782,40 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource { firstGrades to emptyList() }
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { secondGrades to emptyList() }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ true
+ )
+ } returns resourceFlow { firstGrades to emptyList() }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { secondGrades to emptyList() }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
assertEquals(2, items.size)
- assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0) // (from details): 3,5 + 2,5 → 3,0
- assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25
+ assertEquals(
+ 3.0,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // (from details): 3,5 + 2,5 → 3,0
+ assertEquals(
+ 3.25,
+ items.single { it.subject == "Fizyka" }.average,
+ .0
+ ) // (from details): 3,5 + 3,0 → 3,25
}
@Test
@@ -662,12 +824,12 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
firstGrades to listOf(
getSummary(22, "Matematyka", 4.0)
)
}
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
secondGrades to listOf(
getSummary(23, "Matematyka", 3.0)
)
@@ -686,14 +848,40 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource { firstGrades to firstSummaries }
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { secondGrades to secondSummaries.dropLast(1) }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ true
+ )
+ } returns resourceFlow { firstGrades to firstSummaries }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { secondGrades to secondSummaries.dropLast(1) }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
assertEquals(2, items.size)
- assertEquals(3.4, items.single { it.subject == "Matematyka" }.average, .0) // (from summaries): 3,9 + 2,9 → 3,4
- assertEquals(3.05, items.single { it.subject == "Fizyka" }.average, .0) // 3,1 (from summary) + 3,0 (from details) → 3,05
+ assertEquals(
+ 3.4,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // (from summaries): 3,9 + 2,9 → 3,4
+ assertEquals(
+ 3.05,
+ items.single { it.subject == "Fizyka" }.average,
+ .0
+ ) // 3,1 (from summary) + 3,0 (from details) → 3,05
}
@Test
@@ -702,14 +890,40 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource { firstGrades to firstSummaries.dropLast(1) }
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { secondGrades to secondSummaries }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ true
+ )
+ } returns resourceFlow { firstGrades to firstSummaries.dropLast(1) }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { secondGrades to secondSummaries }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
assertEquals(2, items.size)
- assertEquals(3.4, items.single { it.subject == "Matematyka" }.average, .0) // (from summaries): 3,9 + 2,9 → 3,4
- assertEquals(3.45, items.single { it.subject == "Fizyka" }.average, .0) // 3,5 (from details) + 3,4 (from summary) → 3,45
+ assertEquals(
+ 3.4,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // (from summaries): 3,9 + 2,9 → 3,4
+ assertEquals(
+ 3.45,
+ items.single { it.subject == "Fizyka" }.average,
+ .0
+ ) // 3,5 (from details) + 3,4 (from summary) → 3,45
}
@Test
@@ -718,14 +932,40 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource { firstGrades to firstSummaries.dropLast(1) }
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource { secondGrades to secondSummaries }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[1],
+ true
+ )
+ } returns resourceFlow { firstGrades to firstSummaries.dropLast(1) }
+ coEvery {
+ gradeRepository.getGrades(
+ student,
+ semesters[2],
+ true
+ )
+ } returns resourceFlow { secondGrades to secondSummaries }
- val items = runBlocking { gradeAverageProvider.getGradesDetailsWithAverage(student, semesters[2].semesterId, true).getResult() }
+ val items = runBlocking {
+ gradeAverageProvider.getGradesDetailsWithAverage(
+ student,
+ semesters[2].semesterId,
+ true
+ ).getResult()
+ }
assertEquals(2, items.size)
- assertEquals(3.0, items.single { it.subject == "Matematyka" }.average, .0) // (from details): 3,5 + 2,5 → 3,0
- assertEquals(3.25, items.single { it.subject == "Fizyka" }.average, .0) // (from details): 3,5 + 3,0 → 3,25
+ assertEquals(
+ 3.0,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // (from details): 3,5 + 2,5 → 3,0
+ assertEquals(
+ 3.25,
+ items.single { it.subject == "Fizyka" }.average,
+ .0
+ ) // (from details): 3,5 + 3,0 → 3,25
}
@Test
@@ -734,7 +974,7 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
every { preferencesRepository.isOptionalArithmeticAverage } returns false
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
@@ -746,7 +986,7 @@ class GradeAverageProviderTest {
getGrade(22, "Fizyka", 6.0, weight = 2.0)
) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
}
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
@@ -765,7 +1005,7 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
@@ -777,7 +1017,7 @@ class GradeAverageProviderTest {
getGrade(22, "Fizyka", 6.0, weight = 2.0)
) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
}
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
@@ -802,7 +1042,7 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.BOTH_SEMESTERS
coEvery { semesterRepository.getSemesters(student) } returns semesters
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
@@ -814,7 +1054,7 @@ class GradeAverageProviderTest {
getGrade(22, "Fizyka", 6.0, weight = 2.0)
) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
}
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
@@ -839,7 +1079,7 @@ class GradeAverageProviderTest {
every { preferencesRepository.gradeAverageMode } returns GradeAverageMode.ALL_YEAR
coEvery { semesterRepository.getSemesters(student) } returns semesters
- coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns resourceFlow {
listOf(
getGrade(22, "Fizyka", 5.0, weight = 2.0),
getGrade(22, "Fizyka", 6.0, weight = 2.0),
@@ -851,7 +1091,7 @@ class GradeAverageProviderTest {
getGrade(22, "Fizyka", 6.0, weight = 2.0)
) to listOf(getSummary(semesterId = 22, subject = "Fizyka", average = .0))
}
- coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns flowWithResource {
+ coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns resourceFlow {
listOf(
getGrade(23, "Fizyka", 5.0, weight = 1.0),
getGrade(23, "Fizyka", 5.0, weight = 2.0),
@@ -881,9 +1121,9 @@ class GradeAverageProviderTest {
every { preferencesRepository.isOptionalArithmeticAverage } returns false
coEvery { gradeRepository.getGrades(student, semesters[1], true) } returns
- flowWithResource { firstGrades to firstSummaries }
+ resourceFlow { firstGrades to firstSummaries }
coEvery { gradeRepository.getGrades(student, semesters[2], true) } returns
- flowWithResource { listOf() to firstSummaries }
+ resourceFlow { listOf() to firstSummaries }
val items = runBlocking {
gradeAverageProvider.getGradesDetailsWithAverage(
diff --git a/app/src/test/java/io/github/wulkanowy/utils/ResourceUtils.kt b/app/src/test/java/io/github/wulkanowy/utils/ResourceUtils.kt
new file mode 100644
index 000000000..60df1db09
--- /dev/null
+++ b/app/src/test/java/io/github/wulkanowy/utils/ResourceUtils.kt
@@ -0,0 +1,14 @@
+package io.github.wulkanowy.utils
+
+import io.github.wulkanowy.data.Resource
+
+enum class Status {
+ LOADING, SUCCESS, ERROR
+}
+
+val Resource.status
+ get() = when (this) {
+ is Resource.Error -> Status.ERROR
+ is Resource.Loading -> Status.LOADING
+ is Resource.Success -> Status.SUCCESS
+ }
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a0f7639f7..d7e66b5c6 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 744e882ed..1b6c78733 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
#
-# Copyright 2015 the original author or authors.
+# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MSYS* | MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
+ JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=`expr $i + 1`
- done
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
exec "$JAVACMD" "$@"
From c572a91b38e7ca0ac541f2d3f19799408fffef9a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Mar 2022 13:53:42 +0000
Subject: [PATCH 082/669] Bump agconnect-crash from 1.6.4.300 to 1.6.5.200
(#1811)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 7ae210675..c7e42fde8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -242,7 +242,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.300'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.4.300'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.5.200'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From 63380d3e12bfafc4a6275616fb52b9733966d62f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Mar 2022 13:53:57 +0000
Subject: [PATCH 083/669] Bump agcp from 1.6.4.300 to 1.6.5.200 (#1812)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 6b8d14ed9..742d39569 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
- classpath 'com.huawei.agconnect:agcp:1.6.4.300'
+ classpath 'com.huawei.agconnect:agcp:1.6.5.200'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3"
From 2131e892ad705d48626c4e4312a0d7321674be76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Mon, 28 Mar 2022 19:30:20 +0200
Subject: [PATCH 084/669] Add option to select multiple messages to delete
(#1780)
---
.../data/repositories/MessageRepository.kt | 29 +-
.../modules/attendance/AttendanceFragment.kt | 12 +-
.../ui/modules/message/MessageFragment.kt | 45 ++-
.../ui/modules/message/MessagePresenter.kt | 14 +-
.../ui/modules/message/MessageView.kt | 6 +-
.../message/preview/MessagePreviewFragment.kt | 2 +-
.../modules/message/tab/MessageTabAdapter.kt | 154 +++++-----
.../modules/message/tab/MessageTabDataItem.kt | 21 +-
.../modules/message/tab/MessageTabFragment.kt | 123 ++++++--
.../message/tab/MessageTabPresenter.kt | 274 ++++++++++++------
.../ui/modules/message/tab/MessageTabView.kt | 22 +-
.../wulkanowy/ui/modules/more/MoreFragment.kt | 5 +
.../res/drawable/ic_message_select_all.xml | 17 ++
.../res/drawable/ic_message_unselect_all.xml | 12 +
.../res/layout/fragment_message_preview.xml | 4 +-
app/src/main/res/layout/item_message.xml | 19 +-
.../res/menu/action_menu_message_preview.xml | 2 +-
...excuse.xml => context_menu_attendance.xml} | 0
.../res/menu/context_menu_message_tab.xml | 18 ++
app/src/main/res/values-cs/strings.xml | 2 +-
app/src/main/res/values-de/strings.xml | 2 +-
app/src/main/res/values-pl/strings.xml | 2 +-
app/src/main/res/values-ru/strings.xml | 2 +-
app/src/main/res/values-sk/strings.xml | 2 +-
app/src/main/res/values-uk/strings.xml | 2 +-
app/src/main/res/values/strings.xml | 16 +-
26 files changed, 569 insertions(+), 238 deletions(-)
create mode 100644 app/src/main/res/drawable/ic_message_select_all.xml
create mode 100644 app/src/main/res/drawable/ic_message_unselect_all.xml
rename app/src/main/res/menu/{context_menu_excuse.xml => context_menu_attendance.xml} (100%)
create mode 100644 app/src/main/res/menu/context_menu_message_tab.xml
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
index 8d6fd772a..05fb97657 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
@@ -103,7 +103,7 @@ class MessageRepository @Inject constructor(
message: Message,
markAsRead: Boolean = false,
): Flow> = networkBoundResource(
- isResultEmpty = { it == null },
+ isResultEmpty = { it?.message?.content.isNullOrBlank() },
shouldFetch = {
checkNotNull(it) { "This message no longer exist!" }
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
@@ -151,20 +151,27 @@ class MessageRepository @Inject constructor(
recipients = recipients.mapFromEntities()
)
- suspend fun deleteMessage(student: Student, message: Message) {
- val isDeleted = sdk.init(student).deleteMessages(
- messages = listOf(message.messageId), message.folderId
- )
+ suspend fun deleteMessages(student: Student, messages: List) {
+ val folderId = messages.first().folderId
+ val isDeleted = sdk.init(student)
+ .deleteMessages(messages = messages.map { it.messageId }, folderId = folderId)
- if (message.folderId != MessageFolder.TRASHED.id && isDeleted) {
- val deletedMessage = message.copy(folderId = MessageFolder.TRASHED.id).apply {
- id = message.id
- content = message.content
+ if (folderId != MessageFolder.TRASHED.id && isDeleted) {
+ val deletedMessages = messages.map {
+ it.copy(folderId = MessageFolder.TRASHED.id)
+ .apply {
+ id = it.id
+ content = it.content
+ }
}
- messagesDb.updateAll(listOf(deletedMessage))
- } else messagesDb.deleteAll(listOf(message))
+
+ messagesDb.updateAll(deletedMessages)
+ } else messagesDb.deleteAll(messages)
}
+ suspend fun deleteMessage(student: Student, message: Message) =
+ deleteMessages(student, listOf(message))
+
var draftMessage: MessageDraft?
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft))
?.let { json.decodeFromString(it) }
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 84af1ca32..6354b5e04 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
@@ -2,14 +2,8 @@ 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
-import android.view.MenuInflater
-import android.view.MenuItem
-import android.view.View
-import android.view.View.GONE
-import android.view.View.INVISIBLE
-import android.view.View.VISIBLE
+import android.view.*
+import android.view.View.*
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible
@@ -68,7 +62,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag
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)
+ inflater.inflate(R.menu.context_menu_attendance, menu)
return true
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt
index acf3133d9..4607793c9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageFragment.kt
@@ -4,12 +4,14 @@ import android.os.Bundle
import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.core.view.updateLayoutParams
+import androidx.core.view.updateMargins
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
-import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
-import io.github.wulkanowy.data.enums.MessageFolder.SENT
-import io.github.wulkanowy.data.enums.MessageFolder.TRASHED
+import io.github.wulkanowy.data.enums.MessageFolder.*
import io.github.wulkanowy.databinding.FragmentMessageBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.base.BaseFragmentPagerAdapter
@@ -78,7 +80,6 @@ class MessageFragment : BaseFragment(R.layout.fragment_m
}
binding.messageTabLayout.elevation = requireContext().dpToPx(4f)
-
binding.openSendMessageButton.setOnClickListener { presenter.onSendMessageButtonClicked() }
}
@@ -93,12 +94,37 @@ class MessageFragment : BaseFragment(R.layout.fragment_m
binding.messageProgress.visibility = if (show) VISIBLE else INVISIBLE
}
+ override fun showNewMessage(show: Boolean) {
+ binding.openSendMessageButton.run {
+ if (show) show() else hide()
+ }
+ }
+
+ override fun showTabLayout(show: Boolean) {
+ binding.messageTabLayout.isVisible = show
+
+ with(binding.messageViewPager) {
+ isUserInputEnabled = show
+ updateLayoutParams {
+ updateMargins(top = if (show) requireContext().dpToPx(48f).toInt() else 0)
+ }
+ }
+ }
+
+ fun onChildFragmentShowActionMode(show: Boolean) {
+ presenter.onChildViewShowActionMode(show)
+ }
+
fun onChildFragmentLoaded() {
presenter.onChildViewLoaded()
}
- override fun notifyChildMessageDeleted(tabId: Int) {
- (pagerAdapter.getFragmentInstance(tabId) as? MessageTabFragment)?.onParentDeleteMessage()
+ fun onChildFragmentShowNewMessage(show: Boolean) {
+ presenter.onChildViewShowNewMessage(show)
+ }
+
+ fun onFragmentChanged() {
+ presenter.onFragmentChanged()
}
override fun notifyChildLoadData(index: Int, forceRefresh: Boolean) {
@@ -106,6 +132,13 @@ class MessageFragment : BaseFragment(R.layout.fragment_m
?.onParentLoadData(forceRefresh)
}
+ override fun notifyChildrenFinishActionMode() {
+ repeat(3) {
+ (pagerAdapter.getFragmentInstance(it) as? MessageTabFragment)
+ ?.onParentFinishActionMode()
+ }
+ }
+
override fun openSendMessage() {
context?.let { it.startActivity(SendMessageActivity.getStartIntent(it)) }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt
index 9e19517bd..68bdc4b7c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessagePresenter.kt
@@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.message
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@@ -24,6 +23,7 @@ class MessagePresenter @Inject constructor(
fun onPageSelected(index: Int) {
loadChild(index)
+ view?.notifyChildrenFinishActionMode()
}
private fun loadData() {
@@ -35,6 +35,10 @@ class MessagePresenter @Inject constructor(
view?.notifyChildLoadData(index, forceRefresh)
}
+ fun onFragmentChanged() {
+ view?.notifyChildrenFinishActionMode()
+ }
+
fun onChildViewLoaded() {
view?.apply {
showContent(true)
@@ -42,6 +46,14 @@ class MessagePresenter @Inject constructor(
}
}
+ fun onChildViewShowNewMessage(show: Boolean) {
+ view?.showNewMessage(show)
+ }
+
+ fun onChildViewShowActionMode(show: Boolean) {
+ view?.showTabLayout(!show)
+ }
+
fun onSendMessageButtonClicked() {
view?.openSendMessage()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt
index 2aa4d78ec..e0cc5098c 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/MessageView.kt
@@ -12,9 +12,13 @@ interface MessageView : BaseView {
fun showProgress(show: Boolean)
+ fun showNewMessage(show: Boolean)
+
+ fun showTabLayout(show: Boolean)
+
fun notifyChildLoadData(index: Int, forceRefresh: Boolean)
- fun notifyChildMessageDeleted(tabId: Int)
+ fun notifyChildrenFinishActionMode()
fun openSendMessage()
}
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 e1cc2e374..860ecc571 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
@@ -142,7 +142,7 @@ class MessagePreviewFragment :
}
override fun setNotDeletedOptionsLabels() {
- menuDeleteButton?.setTitle(R.string.message_move_to_bin)
+ menuDeleteButton?.setTitle(R.string.message_move_to_trash)
}
override fun showErrorView(show: Boolean) {
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 571cc6d55..af0923b94 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
@@ -2,15 +2,12 @@ package io.github.wulkanowy.ui.modules.message.tab
import android.graphics.Typeface
import android.view.LayoutInflater
-import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.NO_POSITION
import io.github.wulkanowy.R
-import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.databinding.ItemMessageBinding
import io.github.wulkanowy.databinding.ItemMessageChipsBinding
@@ -20,118 +17,141 @@ import javax.inject.Inject
class MessageTabAdapter @Inject constructor() :
RecyclerView.Adapter() {
- enum class ViewType { HEADER, ITEM }
+ var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit = { _, _ -> }
- var onItemClickListener: (Message, position: Int) -> Unit = { _, _ -> }
- var onHeaderClickListener: (chip: CompoundButton, isChecked: Boolean) -> Unit = { _, _ -> }
+ var onLongItemClickListener: (MessageTabDataItem.MessageItem) -> Unit = {}
+
+ var onHeaderClickListener: (CompoundButton, Boolean) -> Unit = { _, _ -> }
var onChangesDetectedListener = {}
private var items = mutableListOf()
- private var onlyUnread: Boolean? = null
- private var onlyWithAttachments = false
- fun setDataItems(
- data: List,
- onlyUnread: Boolean?,
- onlyWithAttachments: Boolean
- ) {
- if (items.size != data.size) onChangesDetectedListener()
+ fun submitData(data: List) {
+ val originalMessagesSize = items.count { it.viewType == MessageItemViewType.MESSAGE }
+ val newMessagesSize = data.count { it.viewType == MessageItemViewType.MESSAGE }
+
+ if (originalMessagesSize != newMessagesSize) onChangesDetectedListener()
+
val diffResult = DiffUtil.calculateDiff(MessageTabDiffUtil(items, data))
items = data.toMutableList()
- this.onlyUnread = onlyUnread
- this.onlyWithAttachments = onlyWithAttachments
+
diffResult.dispatchUpdatesTo(this)
}
- override fun getItemViewType(position: Int): Int {
- return when (position) {
- 0 -> ViewType.HEADER.ordinal
- else -> ViewType.ITEM.ordinal
- }
- }
+ override fun getItemViewType(position: Int) = items[position].viewType.ordinal
override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
- return when (viewType) {
- ViewType.ITEM.ordinal -> ItemViewHolder(
+
+ return when (MessageItemViewType.values()[viewType]) {
+ MessageItemViewType.MESSAGE -> ItemViewHolder(
ItemMessageBinding.inflate(inflater, parent, false)
)
- ViewType.HEADER.ordinal -> HeaderViewHolder(
+ MessageItemViewType.FILTERS -> HeaderViewHolder(
ItemMessageChipsBinding.inflate(inflater, parent, false)
)
- else -> throw IllegalStateException()
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
- is ItemViewHolder -> {
- val item = (items[position] as MessageTabDataItem.MessageItem).message
+ is ItemViewHolder -> bindItemViewHolder(holder, position)
+ is HeaderViewHolder -> bindHeaderViewHolder(holder, position)
+ }
+ }
- with(holder.binding) {
- val style = if (item.unread) Typeface.BOLD else Typeface.NORMAL
+ private fun bindHeaderViewHolder(holder: HeaderViewHolder, position: Int) {
+ val item = items[position] as MessageTabDataItem.FilterHeader
- messageItemAuthor.run {
- text =
- if (item.folderId == MessageFolder.SENT.id) item.recipient else item.sender
- setTypeface(null, style)
- }
- messageItemSubject.run {
- text =
- if (item.subject.isNotBlank()) item.subject else context.getString(R.string.message_no_subject)
- setTypeface(null, style)
- }
- messageItemDate.run {
- text = item.date.toFormattedString()
- setTypeface(null, style)
- }
- messageItemAttachmentIcon.visibility =
- if (item.hasAttachments) View.VISIBLE else View.GONE
+ with(holder.binding) {
+ if (item.onlyUnread == null) {
+ chipUnread.isVisible = false
+ } else {
+ chipUnread.isVisible = true
+ chipUnread.isChecked = item.onlyUnread
+ chipUnread.setOnCheckedChangeListener(onHeaderClickListener)
+ }
+ chipUnread.isEnabled = item.isEnabled
+ chipAttachments.isEnabled = item.isEnabled
+ chipAttachments.isChecked = item.onlyWithAttachments
+ chipAttachments.setOnCheckedChangeListener(onHeaderClickListener)
+ }
+ }
- root.setOnClickListener {
- holder.bindingAdapterPosition.let {
- if (it != NO_POSITION) onItemClickListener(item, it)
- }
+ private fun bindItemViewHolder(holder: ItemViewHolder, position: Int) {
+ val item = (items[position] as MessageTabDataItem.MessageItem)
+ val message = item.message
+
+ with(holder.binding) {
+ val style = if (message.unread) Typeface.BOLD else Typeface.NORMAL
+
+ messageItemAuthor.run {
+ text = if (message.folderId == MessageFolder.SENT.id) {
+ message.recipient
+ } else {
+ message.sender
+ }
+ setTypeface(null, style)
+ }
+ messageItemSubject.run {
+ text = message.subject.ifBlank { context.getString(R.string.message_no_subject) }
+ setTypeface(null, style)
+ }
+ messageItemDate.run {
+ text = message.date.toFormattedString()
+ setTypeface(null, style)
+ }
+ messageItemAttachmentIcon.isVisible = message.hasAttachments
+
+ root.setOnClickListener {
+ holder.bindingAdapterPosition.let {
+ if (it != RecyclerView.NO_POSITION) {
+ onItemClickListener(item, it)
}
}
}
- is HeaderViewHolder -> {
- with(holder.binding) {
- if (onlyUnread == null) chipUnread.isVisible = false
- else {
- chipUnread.isVisible = true
- chipUnread.isChecked = onlyUnread!!
- chipUnread.setOnCheckedChangeListener(onHeaderClickListener)
- }
- chipAttachments.isChecked = onlyWithAttachments
- chipAttachments.setOnCheckedChangeListener(onHeaderClickListener)
- }
+
+ root.setOnLongClickListener {
+ onLongItemClickListener(item)
+ return@setOnLongClickListener true
+ }
+
+ with(messageItemCheckbox) {
+ isChecked = item.isSelected
+ isVisible = item.isActionMode
}
}
}
class ItemViewHolder(val binding: ItemMessageBinding) : RecyclerView.ViewHolder(binding.root)
+
class HeaderViewHolder(val binding: ItemMessageChipsBinding) :
RecyclerView.ViewHolder(binding.root)
private class MessageTabDiffUtil(
private val old: List,
private val new: List
- ) :
- DiffUtil.Callback() {
+ ) : 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
+ val oldItem = old[oldItemPosition]
+ val newItem = new[newItemPosition]
+
+ return if (oldItem is MessageTabDataItem.MessageItem && newItem is MessageTabDataItem.MessageItem) {
+ oldItem.message.id == newItem.message.id
+ } else {
+ oldItem.viewType == newItem.viewType
+ }
}
- override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
- return old[oldItemPosition] == new[newItemPosition]
- }
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
+ old[oldItemPosition] == new[newItemPosition]
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt
index 4f51a936f..634dfc0e7 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt
@@ -2,14 +2,19 @@ package io.github.wulkanowy.ui.modules.message.tab
import io.github.wulkanowy.data.db.entities.Message
-sealed class MessageTabDataItem {
- data class MessageItem(val message: Message) : MessageTabDataItem() {
- override val id = message.id
- }
+sealed class MessageTabDataItem(val viewType: MessageItemViewType) {
- object Header : MessageTabDataItem() {
- override val id = Long.MIN_VALUE
- }
+ data class MessageItem(
+ val message: Message,
+ val isSelected: Boolean,
+ val isActionMode: Boolean
+ ) : MessageTabDataItem(MessageItemViewType.MESSAGE)
- abstract val id: Long
+ data class FilterHeader(
+ val onlyUnread: Boolean?,
+ val onlyWithAttachments: Boolean,
+ val isEnabled: Boolean
+ ) : MessageTabDataItem(MessageItemViewType.FILTERS)
}
+
+enum class MessageItemViewType { FILTERS, MESSAGE }
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 54ee74eb1..654b0e226 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
@@ -3,12 +3,13 @@ package io.github.wulkanowy.ui.modules.message.tab
import android.os.Bundle
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.INVISIBLE
-import android.view.View.VISIBLE
+import android.view.View.*
import android.widget.CompoundButton
+import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView
+import androidx.core.view.updatePadding
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
@@ -20,7 +21,9 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.message.MessageFragment
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
+import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.getThemeAttrColor
+import io.github.wulkanowy.utils.hideSoftInput
import javax.inject.Inject
@AndroidEntryPoint
@@ -31,9 +34,10 @@ class MessageTabFragment : BaseFragment(R.layout.frag
lateinit var presenter: MessageTabPresenter
@Inject
- lateinit var tabAdapter: MessageTabAdapter
+ lateinit var messageTabAdapter: MessageTabAdapter
companion object {
+
const val MESSAGE_TAB_FOLDER_ID = "message_tab_folder_id"
fun newInstance(folder: MessageFolder): MessageTabFragment {
@@ -46,11 +50,38 @@ class MessageTabFragment : BaseFragment(R.layout.frag
}
override val isViewEmpty
- get() = tabAdapter.itemCount == 0
+ get() = messageTabAdapter.itemCount == 0
- override var onlyUnread: Boolean? = false
+ private var actionMode: ActionMode? = null
- override var onlyWithAttachments = false
+ private val actionModeCallback = object : ActionMode.Callback {
+ override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
+ val inflater = mode.menuInflater
+ inflater.inflate(R.menu.context_menu_message_tab, menu)
+ return true
+ }
+
+ override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
+ if (presenter.folder == MessageFolder.TRASHED) {
+ val menuItem = menu.findItem(R.id.messageTabContextMenuDelete)
+ menuItem.setTitle(R.string.message_delete_forever)
+ }
+ return presenter.onPrepareActionMode()
+ }
+
+ override fun onDestroyActionMode(mode: ActionMode) {
+ presenter.onDestroyActionMode()
+ actionMode = null
+ }
+
+ override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean {
+ when (menu.itemId) {
+ R.id.messageTabContextMenuDelete -> presenter.onActionModeSelectDelete()
+ R.id.messageTabContextMenuSelectAll -> presenter.onActionModeSelectCheckAll()
+ }
+ return true
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -69,24 +100,25 @@ class MessageTabFragment : BaseFragment(R.layout.frag
}
override fun initView() {
- with(tabAdapter) {
+ with(messageTabAdapter) {
onItemClickListener = presenter::onMessageItemSelected
+ onLongItemClickListener = presenter::onMessageItemLongSelected
onHeaderClickListener = ::onChipChecked
onChangesDetectedListener = ::resetListPosition
}
with(binding.messageTabRecycler) {
layoutManager = LinearLayoutManager(context)
- adapter = tabAdapter
+ adapter = messageTabAdapter
addItemDecoration(DividerItemDecoration(context, false))
+ itemAnimator = null
}
+
with(binding) {
messageTabSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
messageTabSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
messageTabSwipe.setProgressBackgroundColorSchemeColor(
- requireContext().getThemeAttrColor(
- R.attr.colorSwipeRefresh
- )
+ requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)
)
messageTabErrorRetry.setOnClickListener { presenter.onRetry() }
messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() }
@@ -109,9 +141,28 @@ class MessageTabFragment : BaseFragment(R.layout.frag
})
}
- override fun updateData(data: List, hide: Boolean) {
- if (hide) onlyUnread = null
- tabAdapter.setDataItems(data, onlyUnread, onlyWithAttachments)
+ override fun updateData(data: List) {
+ messageTabAdapter.submitData(data)
+ }
+
+ override fun updateActionModeTitle(selectedMessagesSize: Int) {
+ actionMode?.title = resources.getQuantityString(
+ R.plurals.message_selected_messages_count,
+ selectedMessagesSize,
+ selectedMessagesSize
+ )
+ }
+
+ override fun updateSelectAllMenu(isAllSelected: Boolean) {
+ val menuItem = actionMode?.menu?.findItem(R.id.messageTabContextMenuSelectAll) ?: return
+
+ if (isAllSelected) {
+ menuItem.setTitle(R.string.message_unselect_all)
+ menuItem.setIcon(R.drawable.ic_message_unselect_all)
+ } else {
+ menuItem.setTitle(R.string.message_select_all)
+ menuItem.setIcon(R.drawable.ic_message_select_all)
+ }
}
override fun showProgress(show: Boolean) {
@@ -146,6 +197,14 @@ class MessageTabFragment : BaseFragment(R.layout.frag
binding.messageTabSwipe.isRefreshing = show
}
+ override fun showMessagesDeleted() {
+ showMessage(getString(R.string.message_messages_deleted))
+ }
+
+ override fun notifyParentShowNewMessage(show: Boolean) {
+ (parentFragment as? MessageFragment)?.onChildFragmentShowNewMessage(show)
+ }
+
override fun openMessage(message: Message) {
(activity as? MainActivity)?.pushView(MessagePreviewFragment.newInstance(message))
}
@@ -154,12 +213,16 @@ class MessageTabFragment : BaseFragment(R.layout.frag
(parentFragment as? MessageFragment)?.onChildFragmentLoaded()
}
- fun onParentLoadData(
- forceRefresh: Boolean,
- onlyUnread: Boolean? = this.onlyUnread,
- onlyWithAttachments: Boolean = this.onlyWithAttachments
- ) {
- presenter.onParentViewLoadData(forceRefresh, onlyUnread, onlyWithAttachments)
+ override fun notifyParentShowActionMode(show: Boolean) {
+ (parentFragment as? MessageFragment)?.onChildFragmentShowActionMode(show)
+ }
+
+ fun onParentLoadData(forceRefresh: Boolean) {
+ presenter.onParentViewLoadData(forceRefresh)
+ }
+
+ fun onParentFinishActionMode() {
+ presenter.onParentFinishActionMode()
}
private fun onChipChecked(chip: CompoundButton, isChecked: Boolean) {
@@ -169,8 +232,22 @@ class MessageTabFragment : BaseFragment(R.layout.frag
}
}
- fun onParentDeleteMessage() {
- presenter.onDeleteMessage()
+ override fun showActionMode(show: Boolean) {
+ if (show) {
+ actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback)
+ } else {
+ actionMode?.finish()
+ }
+ }
+
+ override fun showRecyclerBottomPadding(show: Boolean) {
+ binding.messageTabRecycler.updatePadding(
+ bottom = if (show) requireContext().dpToPx(64f).toInt() else 0
+ )
+ }
+
+ override fun hideKeyboard() {
+ activity?.hideSoftInput()
}
override fun onSaveInstanceState(outState: Bundle) {
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 57055a644..870b6433e 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
@@ -12,7 +12,10 @@ import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.consumeAsFlow
+import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import me.xdrop.fuzzywuzzy.FuzzySearch
import timber.log.Timber
@@ -37,6 +40,14 @@ class MessageTabPresenter @Inject constructor(
private val searchChannel = Channel()
+ private val messagesToDelete = mutableSetOf()
+
+ private var onlyUnread: Boolean? = false
+
+ private var onlyWithAttachments = false
+
+ private var isActionMode = false
+
fun onAttachView(view: MessageTabView, folder: MessageFolder) {
super.onAttachView(view)
view.initView()
@@ -47,14 +58,14 @@ class MessageTabPresenter @Inject constructor(
fun onSwipeRefresh() {
Timber.i("Force refreshing the $folder message")
- view?.run { onParentViewLoadData(true, onlyUnread, onlyWithAttachments) }
+ view?.run { loadData(true) }
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
- loadData(true, onlyUnread == true, onlyWithAttachments)
+ loadData(true)
}
}
@@ -62,42 +73,135 @@ class MessageTabPresenter @Inject constructor(
view?.showErrorDetailsDialog(lastError)
}
- fun onDeleteMessage() {
- view?.run { loadData(true, onlyUnread == true, onlyWithAttachments) }
+ fun onParentViewLoadData(forceRefresh: Boolean) {
+ loadData(forceRefresh)
}
- fun onParentViewLoadData(
- forceRefresh: Boolean,
- onlyUnread: Boolean? = view?.onlyUnread,
- onlyWithAttachments: Boolean = view?.onlyWithAttachments == true
- ) {
- loadData(forceRefresh, onlyUnread == true, onlyWithAttachments)
+ fun onParentFinishActionMode() {
+ view?.showActionMode(false)
}
- fun onMessageItemSelected(message: Message, position: Int) {
- Timber.i("Select message ${message.id} item (position: $position)")
- view?.openMessage(message)
+ fun onDestroyActionMode() {
+ isActionMode = false
+ messagesToDelete.clear()
+ updateDataInView()
+
+ view?.run {
+ enableSwipe(true)
+ notifyParentShowNewMessage(true)
+ notifyParentShowActionMode(false)
+ showRecyclerBottomPadding(true)
+ }
+ }
+
+ fun onPrepareActionMode(): Boolean {
+ isActionMode = true
+ messagesToDelete.clear()
+ updateDataInView()
+
+ view?.apply {
+ enableSwipe(false)
+ notifyParentShowNewMessage(false)
+ notifyParentShowActionMode(true)
+ showRecyclerBottomPadding(false)
+ hideKeyboard()
+ }
+ return true
+ }
+
+ fun onActionModeSelectDelete() {
+ Timber.i("Delete ${messagesToDelete.size} messages)")
+ val messageList = messagesToDelete.toList()
+
+ presenterScope.launch {
+ view?.run {
+ showProgress(true)
+ showContent(false)
+ showActionMode(false)
+ }
+
+ runCatching {
+ val student = studentRepository.getCurrentStudent(true)
+ messageRepository.deleteMessages(student, messageList)
+ }
+ .onFailure(errorHandler::dispatch)
+ .onSuccess { view?.showMessagesDeleted() }
+ }
+ }
+
+ fun onActionModeSelectCheckAll() {
+ val messagesToSelect = getFilteredData()
+ val isAllSelected = messagesToDelete.containsAll(messagesToSelect)
+
+ if (isAllSelected) {
+ messagesToDelete.clear()
+ view?.showActionMode(false)
+ } else {
+ messagesToDelete.addAll(messagesToSelect)
+ updateDataInView()
+ }
+
+ view?.run {
+ updateSelectAllMenu(!isAllSelected)
+ updateActionModeTitle(messagesToDelete.size)
+ }
+ }
+
+ fun onMessageItemLongSelected(messageItem: MessageTabDataItem.MessageItem) {
+ if (!isActionMode) {
+ view?.showActionMode(true)
+
+ messagesToDelete.add(messageItem.message)
+
+ view?.updateActionModeTitle(messagesToDelete.size)
+ updateDataInView()
+ }
+ }
+
+ fun onMessageItemSelected(messageItem: MessageTabDataItem.MessageItem, position: Int) {
+ Timber.i("Select message ${messageItem.message.id} item (position: $position)")
+
+ if (!isActionMode) {
+ view?.run {
+ showActionMode(false)
+ openMessage(messageItem.message)
+ }
+ } else {
+ if (!messageItem.isSelected) {
+ messagesToDelete.add(messageItem.message)
+ } else {
+ messagesToDelete.remove(messageItem.message)
+ }
+
+ if (messagesToDelete.isEmpty()) {
+ view?.showActionMode(false)
+ }
+
+ val filteredData = getFilteredData()
+
+ view?.run {
+ updateActionModeTitle(messagesToDelete.size)
+ updateSelectAllMenu(messagesToDelete.containsAll(filteredData))
+ }
+ updateDataInView()
+ }
}
fun onUnreadFilterSelected(isChecked: Boolean) {
view?.run {
onlyUnread = isChecked
- onParentViewLoadData(false, onlyUnread, onlyWithAttachments)
+ loadData(false)
}
}
fun onAttachmentsFilterSelected(isChecked: Boolean) {
view?.run {
onlyWithAttachments = isChecked
- onParentViewLoadData(false, onlyUnread, onlyWithAttachments)
+ loadData(false)
}
}
- private fun loadData(
- forceRefresh: Boolean,
- onlyUnread: Boolean,
- onlyWithAttachments: Boolean
- ) {
+ private fun loadData(forceRefresh: Boolean) {
Timber.i("Loading $folder message data started")
flatResourceFlow {
@@ -106,54 +210,29 @@ class MessageTabPresenter @Inject constructor(
messageRepository.getMessages(student, semester, folder, forceRefresh)
}
.logResourceStatus("load $folder message")
- .onEach {
- when (it) {
- is Resource.Intermediate -> {
- if (it.data.isNotEmpty()) {
- view?.run {
- enableSwipe(true)
- showErrorView(false)
- showRefresh(true)
- showProgress(false)
- showContent(true)
- messages = it.data
- val filteredData = getFilteredData(
- lastSearchQuery,
- onlyUnread,
- onlyWithAttachments
- )
- val messageItems = filteredData.map { message ->
- MessageTabDataItem.MessageItem(message)
- }
- val messageItemsWithHeader =
- listOf(MessageTabDataItem.Header) + messageItems
+ .onResourceData {
+ messages = it
- updateData(
- messageItemsWithHeader,
- folder.id == MessageFolder.SENT.id
- )
- notifyParentDataLoaded()
- }
- }
- }
- is Resource.Success -> {
- messages = it.data
- updateData(
- getFilteredData(
- lastSearchQuery,
- onlyUnread,
- onlyWithAttachments
- )
- )
- analytics.logEvent(
- "load_data",
- "type" to "messages",
- "items" to it.data.size,
- "folder" to folder.name
- )
- }
- else -> {}
+ val filteredData = getFilteredData()
+
+ view?.run {
+ enableSwipe(true)
+ showErrorView(false)
+ showProgress(false)
+ showContent(true)
+ showEmpty(filteredData.isEmpty())
}
+
+ updateDataInView()
+ }
+ .onResourceIntermediate { view?.showRefresh(true) }
+ .onResourceSuccess {
+ analytics.logEvent(
+ "load_data",
+ "type" to "messages",
+ "items" to it.size,
+ "folder" to folder.name
+ )
}
.onResourceNotLoading {
view?.run {
@@ -196,56 +275,71 @@ class MessageTabPresenter @Inject constructor(
.debounce(250)
.map { query ->
lastSearchQuery = query
- val isOnlyUnread = view?.onlyUnread == true
- val isOnlyWithAttachments = view?.onlyWithAttachments == true
- getFilteredData(query, isOnlyUnread, isOnlyWithAttachments)
+
+ getFilteredData()
}
.catch { Timber.e(it) }
.collect {
Timber.d("Applying filter. Full list: ${messages.size}, filtered: ${it.size}")
- updateData(it)
+
+ view?.run {
+ showEmpty(it.isEmpty())
+ showContent(true)
+ showErrorView(false)
+ }
+
+ updateDataInView()
view?.resetListPosition()
}
}
}
- private fun getFilteredData(
- query: String,
- onlyUnread: Boolean = false,
- onlyWithAttachments: Boolean = false
- ): List {
- if (query.trim().isEmpty()) {
+ private fun getFilteredData(): List {
+ if (lastSearchQuery.trim().isEmpty()) {
val sortedMessages = messages.sortedByDescending { it.date }
return when {
- onlyUnread && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
- onlyUnread -> sortedMessages.filter { it.unread == onlyUnread }
+ (onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
+ (onlyUnread == true) -> sortedMessages.filter { it.unread == onlyUnread }
onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments }
else -> sortedMessages
}
} else {
val sortedMessages = messages
- .map { it to calculateMatchRatio(it, query) }
+ .map { it to calculateMatchRatio(it, lastSearchQuery) }
.sortedWith(compareBy> { -it.second }.thenByDescending { it.first.date })
.filter { it.second > 6000 }
.map { it.first }
return when {
- onlyUnread && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
- onlyUnread -> sortedMessages.filter { it.unread == onlyUnread }
+ (onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
+ (onlyUnread == true) -> sortedMessages.filter { it.unread == onlyUnread }
onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments }
else -> sortedMessages
}
}
}
- private fun updateData(data: List) {
- view?.run {
- showEmpty(data.isEmpty())
- showContent(true)
- showErrorView(false)
- val newItems =
- listOf(MessageTabDataItem.Header) + data.map { MessageTabDataItem.MessageItem(it) }
- updateData(newItems, folder.id == MessageFolder.SENT.id)
+ private fun updateDataInView() {
+ val data = getFilteredData()
+
+ val list = buildList {
+ add(
+ MessageTabDataItem.FilterHeader(
+ onlyUnread = onlyUnread.takeIf { folder != MessageFolder.SENT },
+ onlyWithAttachments = onlyWithAttachments,
+ isEnabled = !isActionMode
+ )
+ )
+
+ addAll(data.map { message ->
+ MessageTabDataItem.MessageItem(
+ message = message,
+ isSelected = messagesToDelete.any { it.id == message.id },
+ isActionMode = isActionMode
+ )
+ })
}
+
+ view?.updateData(list)
}
private fun calculateMatchRatio(message: Message, query: String): Int {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt
index a856da3bc..bfa43b209 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt
@@ -7,15 +7,15 @@ interface MessageTabView : BaseView {
val isViewEmpty: Boolean
- var onlyUnread: Boolean?
-
- var onlyWithAttachments: Boolean
-
fun initView()
fun resetListPosition()
- fun updateData(data: List, hide: Boolean)
+ fun updateData(data: List)
+
+ fun updateActionModeTitle(selectedMessagesSize: Int)
+
+ fun updateSelectAllMenu(isAllSelected: Boolean)
fun showProgress(show: Boolean)
@@ -25,8 +25,12 @@ interface MessageTabView : BaseView {
fun showEmpty(show: Boolean)
+ fun showMessagesDeleted()
+
fun showErrorView(show: Boolean)
+ fun notifyParentShowNewMessage(show: Boolean)
+
fun setErrorDetails(message: String)
fun showRefresh(show: Boolean)
@@ -34,4 +38,12 @@ interface MessageTabView : BaseView {
fun openMessage(message: Message)
fun notifyParentDataLoaded()
+
+ fun notifyParentShowActionMode(show: Boolean)
+
+ fun hideKeyboard()
+
+ fun showActionMode(show: Boolean)
+
+ fun showRecyclerBottomPadding(show: Boolean)
}
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 145b12a35..df55abc9c 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
@@ -89,6 +89,11 @@ class MoreFragment : BaseFragment(R.layout.fragment_more),
if (::presenter.isInitialized) presenter.onViewReselected()
}
+ override fun onFragmentChanged() {
+ (parentFragmentManager.fragments.find { it is MessageFragment } as MessageFragment?)
+ ?.onFragmentChanged()
+ }
+
override fun updateData(data: List>) {
with(moreAdapter) {
items = data
diff --git a/app/src/main/res/drawable/ic_message_select_all.xml b/app/src/main/res/drawable/ic_message_select_all.xml
new file mode 100644
index 000000000..eab195d94
--- /dev/null
+++ b/app/src/main/res/drawable/ic_message_select_all.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_message_unselect_all.xml b/app/src/main/res/drawable/ic_message_unselect_all.xml
new file mode 100644
index 000000000..c388522e4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_message_unselect_all.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_message_preview.xml b/app/src/main/res/layout/fragment_message_preview.xml
index 4b15cf108..5980d820b 100644
--- a/app/src/main/res/layout/fragment_message_preview.xml
+++ b/app/src/main/res/layout/fragment_message_preview.xml
@@ -9,8 +9,10 @@
android:id="@+id/messagePreviewRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:visibility="gone"
tools:itemCount="1"
- tools:listitem="@layout/item_message_preview" />
+ tools:listitem="@layout/item_message_preview"
+ tools:visibility="visible" />
+
+
@@ -45,6 +57,7 @@
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@id/messageItemAttachmentIcon"
+ app:layout_constraintStart_toEndOf="@id/messageItemCheckbox"
app:layout_constraintStart_toStartOf="@id/messageItemAuthor"
app:layout_constraintTop_toBottomOf="@+id/messageItemAuthor"
app:layout_goneMarginEnd="0dp"
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 4c1332e10..5011e2356 100644
--- a/app/src/main/res/menu/action_menu_message_preview.xml
+++ b/app/src/main/res/menu/action_menu_message_preview.xml
@@ -19,7 +19,7 @@
android:id="@+id/messagePreviewMenuDelete"
android:icon="@drawable/ic_menu_message_delete"
android:orderInCategory="1"
- android:title="@string/message_delete"
+ android:title="@string/message_move_to_trash"
app:iconTint="@color/material_on_surface_emphasis_medium"
app:showAsAction="ifRoom" />
-
+
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index e7c2da332..169beed07 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -287,7 +287,7 @@
Odpověď
Poslat dále
Odstranit
- Přesunout do koše
+ Přesunout do koše
Odstranit natrvalo
Zpráva byla úspěšně odstraněna
Sdílet
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 68c371d51..a97019446 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -253,7 +253,7 @@
Antwort
Weiterleiten
Löschen
- In den Korb wandern
+ In den Korb wandern
Dauerhaft löschen
Nachricht erfolgreich gelöscht
Teilen
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index da127fe2d..85018247f 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -287,7 +287,7 @@
Odpowiedz
Prześlij dalej
Usuń
- Przenieś do kosza
+ Przenieś do kosza
Usuń trwale
Wiadomość usunięta pomyślnie
Udostępnij
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index e6e9834f5..1b3553f1e 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -287,7 +287,7 @@
Ответ
Переслать
Удалить
- Перенести в корзину
+ Перенести в корзину
Удалить навсегда
Сообщение успешно удалено
Поделиться
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index ba701eafd..d33b86654 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -287,7 +287,7 @@
Odpoveď
Poslať ďalej
Odstrániť
- Presunúť do koša
+ Presunúť do koša
Odstrániť natrvalo
Správa bola úspešne odstránená
Zdieľať
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 7bcffe01b..ec2427c95 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -287,7 +287,7 @@
Відповісти
Переслати
Видалити
- Перемістити у кошик
+ Перемістити у кошик
Видалити назавжди
Повідомлення було успішно видалено
Поділіться
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ee702251e..2763f00de 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -277,11 +277,12 @@
No messages
From:
To:
- Date: %s
+ Date: %1$s
Reply
Forward
- Delete
- Move to trash
+ Select all
+ Unselect all
+ Move to trash
Delete permanently
Message deleted successfully
Share
@@ -297,8 +298,8 @@
Read: %s
Read by: %1$d of %2$d people
-
- %d message
- - %d messages
+ - %1$d message
+ - %1$d messages
- New message
@@ -310,6 +311,11 @@
- You received %1$d message
- You received %1$d messages
+
+ - %1$d selected
+ - %1$d selected
+
+ Messages deleted
From df58aa78aefc610ba14ba083fc87ee297df19b8d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Thu, 31 Mar 2022 09:00:41 +0200
Subject: [PATCH 085/669] New Crowdin updates (#1783)
---
app/src/main/res/values-cs/strings.xml | 31 +++++++++++++++++---------
app/src/main/res/values-de/strings.xml | 25 ++++++++++++++-------
app/src/main/res/values-pl/strings.xml | 27 +++++++++++++++-------
app/src/main/res/values-ru/strings.xml | 29 ++++++++++++++++--------
app/src/main/res/values-sk/strings.xml | 31 +++++++++++++++++---------
app/src/main/res/values-uk/strings.xml | 29 ++++++++++++++++--------
6 files changed, 118 insertions(+), 54 deletions(-)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 169beed07..555da8dfa 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -55,7 +55,7 @@
Neplatný symbol
Žák nebyl nalezen. Zkontrolujte správnost symbolu a vybrané varianty deníku UONET+
Vybraný žák je už přihlášen
- Symbol najdete na stránce deníku v Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUjistěte se, že jste na předchozí obrazovce nastavili správnou variantu deníku do pole Variace deníku UONET+. Wulkanowy v tuto chvíli nezjistí předškolní żaków
+ Symbol najdete na stránce deníku v Uczeń→ Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUjistěte se, že jste na předchozí obrazovce nastavili správnou variantu deníku do pole Variace deníku UONET+
Vyberte žáky, kteří se mají do aplikace přihlásit
Jiné možnosti
V tomto režimu nefungují následující: šťastné číslo, statistiky třídy, shrnutí frekvencí, ospravedlnění nepřítomnosti, dokončené lekce, informace o škole a prohlížení seznamu registrovaných zařízení
@@ -105,7 +105,9 @@
Semestr
Body
Vysvětlivky
- Průměr: %1$s
+ Průměr třídy: %1$s
+ Váš průměr: %1$s
+ Vaše známka: %1$s
Třída
Žák
@@ -283,10 +285,11 @@
Žádné zprávy
Od:
Komu:
- Datum: %s
+ Datum: %1$s
Odpověď
Poslat dále
- Odstranit
+ Vybrat vše
+ Odznačit vše
Přesunout do koše
Odstranit natrvalo
Zpráva byla úspěšně odstraněna
@@ -303,10 +306,10 @@
Přečtena: %s
Přečtena přes: %1$d z %2$d osob
- - %d zpráva
- - %d zprávy
- - %d zpráv
- - %d zpráv
+ - %1$d zpráva
+ - %1$d zprávy
+ - %1$d zpráv
+ - %1$d zpráv
- Nová zpráva
@@ -322,6 +325,13 @@
- Máte %1$d nových zpráv
- Máte %1$d nových zpráv
+
+ - %1$d vybraná
+ - %1$d vybrané
+ - %1$d vybraných
+ - %1$d vybraných
+
+ Zprávy odstraněné
Žádné informace o poznámkách
Body
@@ -383,8 +393,8 @@
Žádné informace o domácích úkolech
- Označit jako hotové
- Neudělané
+ Vykonané
+ Nevykonané
Přidat domácí úkol
Domácí úkol byl úspěšně přidán
Domácí úkol byl úspěšně odstraněn
@@ -647,6 +657,7 @@
Zkopírováno
Vrátit
Změnit
+ Přidat do kalendáře
Žádné lekce
Vybrat motiv
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index a97019446..8492f6460 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -55,7 +55,7 @@
Ungültige symbol
Schüler nicht gefunden. Überprüfen Sie das Symbol und die gewählte Variation des UONET+ Registers
Ausgewählter Student ist bereits angemeldet.
- Das Symbol kann auf der Registerseite in Uczeń→ Dostęp Mobilny → Zarejestruj urządzenie mobilnegefunden werden.\n\nStellen Sie sicher, dass Sie die entsprechende Registervariante im Feld UONET+ Registervariante auf dem vorherigen Bildschirm festgelegt haben. Wulkanowy erkennt zur Zeit keine Vorschulstudenten
+ Das Symbol kann auf der Registerseite in Uczeń→ Dostęp Mobilny → Zarejestruj urządzenie mobilnegefunden werden.\n\nStellen Sie sicher, dass Sie die entsprechende Registervariante im Feld UONET+ Registervariante auf dem vorherigen Bildschirm festgelegt haben
Wählen Sie die Studenten aus, die sich bei der Anwendung anmelden sollen
Andere Optionen
In diesem Modus funktioniert eine Glücknummer, eine Klassenstatistik, eine Zusammenfassung der Anwesenheit, eine Entschuldigung für die Abwesenheit, abgeschlossene Lektionen, Schulinformationen und eine Vorschau der Liste der registrierten Geräte nicht
@@ -105,7 +105,9 @@
Semester
Punkte
Legende
- Durchschnitt: %1$s
+ Klassendurchschnitt: %1$s
+ Dein Durchschnitt: %1$s
+ Deine Note: %1$s
Klasse
Schüler
@@ -249,11 +251,12 @@
Keine Nachrichten
Von:
An:
- Datum: %s
+ Datum: %1$s
Antwort
Weiterleiten
- Löschen
- In den Korb wandern
+ Alle auswählen
+ Alle abwählen
+ In Papierkorb verschieben
Dauerhaft löschen
Nachricht erfolgreich gelöscht
Teilen
@@ -269,8 +272,8 @@
Lesen: %s
Lesen von: %1$d von %2$d Personen
- - %d nachricht
- - %d nachrichten
+ - %1$d Nachricht
+ - %1$d Nachrichten
- Neu nachricht
@@ -282,6 +285,11 @@
- Du hast %1$d nachricht bekommen
- Du hast %1$d nachrichten bekommen
+
+ - %1$d ausgewählt
+ - %1$d ausgewählt
+
+ Nachrichten gelöscht
Keine Informationen über Eintragen
Punkte
@@ -561,6 +569,7 @@
Kopiert
lösen
Ändern
+ Zum Kalender hinzufügen
Keine Lektionen
Thema wählen
@@ -597,7 +606,7 @@
Offizielle App-Benachrichtigungen erfassen
Entfernen Sie offizielle App-Benachrichtigungen nach der Erfassung
Benachrichtigungen erfassen
- With this feature you can gain a substitute of push notifications like in the official app. All you need to do is allow Wulkanowy to receive all notifications in your system settings.\n\nHow it works?\nWhen you get a notification in Dziennik VULCAN, Wulkanowy will be notified (that\'s what these extra permissions are for) and will trigger a sync so that can send its own notification.\n\nFOR ADVANCED USERS ONLY
+ Mit dieser Funktion können Sie einen Ersatz für Push-Benachrichtigungen erhalten, wie in der offiziellen App. Alles, was Sie tun müssen, ist es Wulkanowy erlauben, alle Benachrichtigungen in Ihren Systemeinstellungen zu erhalten.\n\nWie funktioniert es?\nWenn Sie eine Benachrichtigung in Dziennik VULCAN erhalten, Wulkanowy wird benachrichtigt (dafür sind diese zusätzlichen Berechtigungen) und wird eine Synchronisierung auslösen, so dass eine eigene Benachrichtigung gesendet werden kann.\n\nNUR FÜR FORTGESCHRITTENE BENUTZER
Bevorstehende Unterrichtsbenachrichtigungen
Sie müssen der Wulkanowy-App erlauben, in Ihren Systemeinstellungen Alarme und Erinnerungen einzustellen, damit diese Funktion verwendet werden kann.
Gehe zu den Einstellungen
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 85018247f..2c8a83cca 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -55,7 +55,7 @@
Nieprawidłowy symbol
Nie znaleziono ucznia. Sprawdź poprawność symbolu i wybranej odmiany dziennika UONET+
Wybrany uczeń jest już zalogowany
- Symbol znajdziesz na stronie dziennika w Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUpewnij się, że w polu Dziennik UONET+ na poprzednim ekranie została ustawiona odpowiednia odmiana dziennika.\n\nWulkanowy na chwilę obecną nie wykrywa uczniów przedszkolnych (z zerówki)
+ Symbol znajdziesz na stronie dziennika w Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUpewnij się, że w polu Dziennik UONET+ na poprzednim ekranie została ustawiona odpowiednia odmiana dziennika
Wybierz uczniów do zalogowania w aplikacji
Inne opcje
W tym trybie nie działa szczęśliwy numerek, uczeń na tle klasy, podsumowanie frekwencji, usprawiedliwianie nieobecności, lekcje zrealizowane, informacje o szkole i podgląd listy zarejestrowanych urządzeń
@@ -105,7 +105,9 @@
Semestralne
Punkty
Legenda
- Średnia: %1$s
+ Średnia klasy: %1$s
+ Twoja średnia: %1$s
+ Twoja ocena: %1$s
Klasa
Uczeń
@@ -283,10 +285,11 @@
Brak wiadomości
Od:
Do:
- Data: %s
+ Data: %1$s
Odpowiedz
Prześlij dalej
- Usuń
+ Zaznacz wszystkie
+ Odznacz wszystkie
Przenieś do kosza
Usuń trwale
Wiadomość usunięta pomyślnie
@@ -303,10 +306,10 @@
Przeczytana: %s
Przeczytana przez: %1$d z %2$d osób
- - %d wiadomość
- - %d wiadomości
- - %d wiadomości
- - %d wiadomości
+ - %1$d wiadomość
+ - %1$d wiadomości
+ - %1$d wiadomości
+ - %1$d wiadomości
- Nowa wiadomość
@@ -322,6 +325,13 @@
- Masz %1$d nowych wiadomości
- Masz %1$d nowych wiadomości
+
+ - %1$d wybrana
+ - %1$d wybrane
+ - %1$d wybranych
+ - %1$d wybranych
+
+ Wiadomości zostały usunięte
Brak informacji o uwagach
Punkty
@@ -647,6 +657,7 @@
Skopiowano
Cofnij
Zmień
+ Dodaj do kalendarza
Brak lekcji
Wybierz motyw
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 1b3553f1e..1ddcaf4c5 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -105,7 +105,9 @@
За семестр
Баллы
Легенда
- Средняя: %1$s
+ Средняя класса: %1$s
+ Ваша средний: %1$s
+ Ваша оценка: %1$s
Класс
Студент
@@ -283,10 +285,11 @@
Нет сообщений
От:
Кому:
- Дата: %s
+ Дата: %1$s
Ответ
Переслать
- Удалить
+ Выбрать всё
+ Снять выбор
Перенести в корзину
Удалить навсегда
Сообщение успешно удалено
@@ -303,10 +306,10 @@
Чтение: %s
Прочитано: %1$d из %2$d человек
- - %d сообщение
- - %d сообщения
- - %d сообщений
- - %d сообщений
+ - %1$d сообщение
+ - %1$d сообщений
+ - %1$d сообщений
+ - %1$d сообщений
- Новое сообщение
@@ -322,6 +325,13 @@
- Вы получили %1$d новых сообщений
- Вы получили %1$d новых сообщений
+
+ - %1$d выбрано
+ - %1$d выбрано
+ - %1$d выбрано
+ - %1$d выбрано
+
+ Сообщение удалено
Нет информации о заметках
Баллы
@@ -383,8 +393,8 @@
Нет домашних заданий
- Отметить как выполненное
- Отметить как невыполненное
+ Завершено
+ Не завершено
Добавить домашнюю работу
Домашняя работа успешно добавлена
Домашняя работа успешно удалена
@@ -647,6 +657,7 @@
Скопировано
Отменить
Изменить
+ Добавить в календарь
Нет уроков
Выбрать тему
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index d33b86654..804473add 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -55,7 +55,7 @@
Neplatný symbol
Žiak nebol nájdený. Skontrolujte správnosť symbolu a vybrané varianty denníka UONET+
Vybraný žiak už je prihlásený
- Symbol nájdete na stránke denníka v Uczeń→ Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUistite sa, že ste na predchádzajúcu obrazovke nastaviť správny variant denníka do poľa Variácie denníka UONET+. Wulkanowy v túto chvíľu nezistí predškolské żaków
+ Symbol nájdete na stránke denníka v Uczeń→ Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nUistite sa, že ste na predchádzajúcu obrazovke nastaviť správny variant denníka do poľa Variácie denníka UONET+
Vyberte žiakov, ktorí sa majú do aplikácie prihlásiť
Iné možnosti
V tomto režime nefungujú nasledovné: šťastné číslo, štatistiky triedy, zhrnutie frekvencií, ospravedlnenie neprítomnosti, dokončené lekcie, informácie o škole a prezeranie zoznamu registrovaných zariadení
@@ -105,7 +105,9 @@
Semester
Body
Vysvetlivky
- Priemer: %1$s
+ Priemer triedy: %1$s
+ Váš priemer: %1$s
+ Vaša známka: %1$s
Trieda
Žiák
@@ -283,10 +285,11 @@
Žiadne správy
Od:
Komu:
- Dátum: %s
+ Dátum: %1$s
Odpoveď
Poslať ďalej
- Odstrániť
+ Vybrať všetko
+ Odznačiť všetko
Presunúť do koša
Odstrániť natrvalo
Správa bola úspešne odstránená
@@ -303,10 +306,10 @@
Prečítaná: %s
Prečítaná cez: %1$d z %2$d osôb
- - %d správa
- - %d správy
- - %d správ
- - %d správ
+ - %1$d správa
+ - %1$d správy
+ - %1$d správ
+ - %1$d správ
- Nová správa
@@ -322,6 +325,13 @@
- Máte %1$d nových správ
- Máte %1$d nových správ
+
+ - %1$d vybraná
+ - %1$d vybrané
+ - %1$d vybraných
+ - %1$d vybraných
+
+ Správy odstránené
Žiadne informácie o poznámkach
Body
@@ -383,8 +393,8 @@
Žiadne informácie o domácich úlohách
- Označiť ako hotové
- Nevyrobené
+ Vykonané
+ Nevykonané
Pridať domácu úlohu
Domáca úloha bola úspešně pridaná
Domáca úloha bola úspešně odstránená
@@ -647,6 +657,7 @@
Skopírované
Vrátiť
Zmeniť
+ Pridať do kalendára
Žiadne lekcie
Vybrať motív
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index ec2427c95..c53161e71 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -55,7 +55,7 @@
Неправильний симбвол
Студента не знайдено Перевірте символ та обраний варіант реєстру UONET+
Даного учня вже авторизовано
- Символ можна знайти на сторінці реєстру в Учень → Мобільний доступ → Додайте мобільне приладдя .\n\nПереконайтесь, що ви встановили відповідний варіант реєстру в полі UONET + варіант реєстрації на попередньому екрані. На даний момент Wulkanowy не виявляє учнів дошкільних закладів
+ Символ можна знайти на сторінці реєстрації в Учень → Мобільний доступ → Додайте мобільне приладдя .\n\nПереконайтесь, що ви встановили відповідний варіант реєстру в полі UONET + варіант реєстрації на попередньому екрані. На даний момент Wulkanowy не виявляє учнів дошкільних закладів
Виберіть учнів для авторизації в додатку
Інші варіанти
У цьому режимі не працюють: щасливий номер, статистика класу по оцінкам, статистика відвідуваності і уроків, інформація про школу і список зареєстрованних пристроїв
@@ -105,7 +105,9 @@
Семестрові
Бали
Умовні позначення
- Середня оцінка: %1$s
+ Середня классу: %1$s
+ Ваша середня: %1$s
+ Ваша оцінка: %1$s
Клас
Учень
@@ -283,11 +285,12 @@
Нема повідомлень
Від:
Кому:
- Дата: %s
+ Дата: %1$s
Відповісти
Переслати
- Видалити
- Перемістити у кошик
+ Вибрати все
+ Відмінити вибір
+ Перемістити до кошика
Видалити назавжди
Повідомлення було успішно видалено
Поділіться
@@ -303,10 +306,10 @@
Читання: %s
Прочитанно:%1$d через %2$d людей
- - %d повідомлення
- - %d повідомлення
- - %d повідомлень
- - %d повідомлень
+ - %1$d повідомлення
+ - %1$d повідомлень
+ - %1$d повідомлень
+ - %1$d повідомлень
- Нове повідомлення
@@ -322,6 +325,13 @@
- Ви отримали %1$d нових повідомлень
- Ви отримали %1$d нових повідомлень
+
+ - %1$d вибрано
+ - вибрано %1$d
+ - вибрано %1$d
+ - %1$d вибрано
+
+ Повідомлення видалені
Брак інформації о зауваженнях
Бали
@@ -647,6 +657,7 @@
Скопійовано
Відмінити
Змінити
+ Додати у календар
Брак уроків
Увібрати тему
From 884d443c5ba180f1f5cbd6f27d97434a2ad75b9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 2 Apr 2022 22:01:34 +0200
Subject: [PATCH 086/669] Version 1.6.0
---
app/build.gradle | 6 +++---
app/src/main/play/release-notes/pl-PL/default.txt | 11 ++++++-----
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index c7e42fde8..9c056c542 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -22,8 +22,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 31
- versionCode 103
- versionName "1.5.0"
+ versionCode 104
+ versionName "1.6.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -178,7 +178,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:fba873461e"
+ implementation "io.github.wulkanowy:sdk:1.6.0"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
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 6e768ac3a..f66c25495 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,9 @@
-Wersja 1.5.0
+Wersja 1.6.0
-- dodaliśmy możliwość dodawania własnych lekcji dodatkowych
-- dodaliśmy wsparcie dla różnych stref czasowych
-- dodaliśmy eksperymentalne wsparcie dla przedszkola
-- wprowadziliśmy też wiele innych mniejszych poprawek, poprawiających komfort używania aplikacji
+- dodaliśmy możliwość usuwania wielu wiadomości jednocześnie
+- dodaliśmy opcję szybkiego dodawania sprawdzianów do kalendarza
+- dodaliśmy średnią ucznia w wykresach ocen klasy
+- naprawiliśmy rzadki błąd dotyczący problemów z automatycznym odświeżaniem ekranu startowego
+- naprawiliśmy błąd z liczeniem średniej w drugim semestrze
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
From 84067126a1d711f0232adad5ada3956df4c2c884 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 5 Apr 2022 08:09:46 +0000
Subject: [PATCH 087/669] Bump hianalytics from 6.4.1.300 to 6.4.1.301 (#1819)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 9c056c542..92e160106 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
playImplementation 'com.google.android.play:core-ktx:1.8.1'
playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
- hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.300'
+ hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.301'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.5.200'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From 3347e8fba8c42bb5d40cd496bbd4b2891b97235f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 5 Apr 2022 08:10:07 +0000
Subject: [PATCH 088/669] Bump kotlin_version from 1.6.10 to 1.6.20 (#1818)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 742d39569..9aa30944f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
buildscript {
ext {
- kotlin_version = '1.6.10'
+ kotlin_version = '1.6.20'
about_libraries = '8.9.4'
hilt_version = "2.41"
}
From 6531061b48449693965fea7d8bc64fea90592dca Mon Sep 17 00:00:00 2001
From: Mat Lee <98318975+zlewchan@users.noreply.github.com>
Date: Tue, 5 Apr 2022 10:30:49 +0200
Subject: [PATCH 089/669] Fix text aligment in exam dialog (#1821)
---
app/src/main/res/layout/dialog_exam.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/res/layout/dialog_exam.xml b/app/src/main/res/layout/dialog_exam.xml
index 57ed9d45b..0d04b1fac 100644
--- a/app/src/main/res/layout/dialog_exam.xml
+++ b/app/src/main/res/layout/dialog_exam.xml
@@ -146,7 +146,7 @@
android:id="@+id/examDialogDeadlineDateValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="0dp"
+ android:layout_marginStart="16dp"
android:layout_marginEnd="24dp"
android:paddingStart="0dp"
android:paddingEnd="16dp"
@@ -162,7 +162,7 @@
android:id="@+id/examDialogEntryDateTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="0dp"
+ android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:text="@string/exam_entry_date"
From d473d53879385891aefcb4d9a560ef3e34c6e5a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Tue, 5 Apr 2022 13:56:11 +0200
Subject: [PATCH 090/669] Add standard register variant as translatable string
(#1824)
---
app/src/main/res/values/api_hosts.xml | 2 +-
app/src/main/res/values/strings.xml | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values/api_hosts.xml b/app/src/main/res/values/api_hosts.xml
index b3b434e14..8413d68e4 100644
--- a/app/src/main/res/values/api_hosts.xml
+++ b/app/src/main/res/values/api_hosts.xml
@@ -1,7 +1,7 @@
- - Standardowa
+ - @string/login_host_standard
- Opolska eSzkoła
- Gdańska Platforma Edukacyjna
- Lubelski Portal Oświatowy
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2763f00de..ff25da7f5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -77,6 +77,7 @@
Recover your account
Recover
Student is already signed in
+ Standard
From 679cf2554de22d824cd39fbc6ac3db94bbb96686 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Wed, 6 Apr 2022 08:01:11 +0200
Subject: [PATCH 091/669] Fix text in lucky number widget (#1825)
---
.../ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt
index e016c07e6..e03e3e90e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/luckynumberwidget/LuckyNumberWidgetProvider.kt
@@ -78,7 +78,7 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
.apply {
setTextViewText(
R.id.luckyNumberWidgetNumber,
- luckyNumber.dataOrNull?.toString() ?: "#"
+ luckyNumber.dataOrNull?.luckyNumber?.toString() ?: "#"
)
setOnClickPendingIntent(R.id.luckyNumberWidgetContainer, appIntent)
}
From cb4ae21903680b4fc0c78496f81fe3727748ee4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Wed, 6 Apr 2022 08:01:41 +0200
Subject: [PATCH 092/669] Replace Serializable to Parcelable in Destination
(#1823)
---
.../wulkanowy/ui/modules/Destination.kt | 19 +++++++++++++++++--
.../wulkanowy/ui/modules/main/MainActivity.kt | 2 +-
.../ui/modules/splash/SplashActivity.kt | 2 +-
3 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
index f49c48891..f8c456fe6 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
@@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules
+import android.os.Parcelable
import androidx.fragment.app.Fragment
import io.github.wulkanowy.data.serializers.LocalDateSerializer
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
@@ -15,11 +16,12 @@ import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
+import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import java.time.LocalDate
@Serializable
-sealed class Destination private constructor() : java.io.Serializable {
+sealed class Destination private constructor() : Parcelable {
/*
Type in children classes have to be as getter to avoid null in enums
@@ -45,30 +47,35 @@ sealed class Destination private constructor() : java.io.Serializable {
MESSAGE(Message);
}
+ @Parcelize
@Serializable
object Dashboard : Destination() {
override val type get() = Type.DASHBOARD
override val fragment get() = DashboardFragment.newInstance()
}
+ @Parcelize
@Serializable
object Grade : Destination() {
override val type get() = Type.GRADE
override val fragment get() = GradeFragment.newInstance()
}
+ @Parcelize
@Serializable
object Attendance : Destination() {
override val type get() = Type.ATTENDANCE
override val fragment get() = AttendanceFragment.newInstance()
}
+ @Parcelize
@Serializable
object Exam : Destination() {
override val type get() = Type.EXAM
override val fragment get() = ExamFragment.newInstance()
}
+ @Parcelize
@Serializable
data class Timetable(
@Serializable(with = LocalDateSerializer::class)
@@ -78,51 +85,59 @@ sealed class Destination private constructor() : java.io.Serializable {
override val fragment get() = TimetableFragment.newInstance(date)
}
+ @Parcelize
@Serializable
object Homework : Destination() {
override val type get() = Type.HOMEWORK
override val fragment get() = HomeworkFragment.newInstance()
}
+ @Parcelize
@Serializable
object Note : Destination() {
override val type get() = Type.NOTE
override val fragment get() = NoteFragment.newInstance()
}
+ @Parcelize
@Serializable
object Conference : Destination() {
override val type get() = Type.CONFERENCE
override val fragment get() = ConferenceFragment.newInstance()
}
+ @Parcelize
@Serializable
object SchoolAnnouncement : Destination() {
override val type get() = Type.SCHOOL_ANNOUNCEMENT
override val fragment get() = SchoolAnnouncementFragment.newInstance()
}
+ @Parcelize
@Serializable
object School : Destination() {
override val type get() = Type.SCHOOL
override val fragment get() = SchoolFragment.newInstance()
}
+ @Parcelize
@Serializable
object LuckyNumber : Destination() {
override val type get() = Type.LUCKY_NUMBER
override val fragment get() = LuckyNumberFragment.newInstance()
}
+ @Parcelize
@Serializable
object More : Destination() {
override val type get() = Type.MORE
override val fragment get() = MoreFragment.newInstance()
}
+ @Parcelize
@Serializable
object Message : Destination() {
override val type get() = Type.MESSAGE
override val fragment get() = MessageFragment.newInstance()
}
-}
\ No newline at end of file
+}
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 0cd38ac7d..d1f324475 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
@@ -86,7 +86,7 @@ class MainActivity : BaseActivity(), MainVie
messageContainer = binding.mainMessageContainer
updateHelper.messageContainer = binding.mainFragmentContainer
- val destination = (intent.getSerializableExtra(EXTRA_START_DESTINATION) as Destination?)
+ val destination = (intent.getParcelableExtra(EXTRA_START_DESTINATION) as Destination?)
?.takeIf { savedInstanceState == null }
presenter.onAttachView(this, destination)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt
index a86024e49..24347e735 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt
@@ -45,7 +45,7 @@ class SplashActivity : BaseActivity(), SplashView
installSplashScreen().setKeepOnScreenCondition { true }
val externalLink = intent?.getStringExtra(EXTRA_EXTERNAL_URL)
- val startDestination = intent?.getSerializableExtra(EXTRA_START_DESTINATION) as Destination?
+ val startDestination = intent?.getParcelableExtra(EXTRA_START_DESTINATION) as Destination?
?: shortcutsHelper.getDestination(intent)
presenter.onAttachView(this, externalLink, startDestination)
From 0a87df3d827f7f955366c45c1bccc1e13226fe1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Wed, 6 Apr 2022 17:19:38 +0200
Subject: [PATCH 093/669] New Crowdin updates (#1817)
---
app/src/main/res/values-cs/strings.xml | 7 ++++---
app/src/main/res/values-de/strings.xml | 1 +
app/src/main/res/values-pl/strings.xml | 1 +
app/src/main/res/values-ru/strings.xml | 1 +
app/src/main/res/values-sk/strings.xml | 1 +
app/src/main/res/values-uk/strings.xml | 1 +
6 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 555da8dfa..11054a0e5 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -71,6 +71,7 @@
Obnovte svůj účet
Obnovit
Žák je už přihlášen
+ Standardní
Manažer účtů
Přihlásit se
@@ -93,9 +94,9 @@
Předpokládaná známka
Vypočítaný průměr
Jak funguje vypočítaný průměr?
- Vypočítaný průměr je aritmetický průměr vypočítaný z průměrů předmětů. Umožňuje vám to znát přibližný konečný průměr. Vypočítává se způsobem zvoleným uživatelem v nastavení aplikaci. Doporučuje se vybrat příslušnou možnost. Důvodem je rozdílný výpočet školních průměrů. Pokud vaše škola navíc uvádí průměr předmětů na stránce deníku Vulcan, aplikace si je stáhne a tyto průměry nepočítá. To lze změnit vynucením výpočtu průměru v nastavení aplikaci.\n\nPrůměr známek pouze z vybraného semestru:\n1. Výpočet váženého průměru pro každý předmět v daném semestru\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů\n\nPrůměr průměrů z obou semestrů:\n1. Výpočet váženého průměru pro každý předmět v semestru 1 a 2\n2. Výpočet aritmetického průměru vypočítaných průměrů za semestry 1 a 2 pro každý předmět.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru součtených průměrů\n\nPrůměr známek z celého roku:\n1. Výpočet váženého průměru za rok pro každý předmět. Konečný průměr v 1. semestru je nepodstatný.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru součtených průměrů
+ Vypočítaný průměr je aritmetický průměr vypočítaný z průměrů předmětů. Umožňuje vám to znát přibližný konečný průměr. Vypočítává se způsobem zvoleným uživatelem v nastavení aplikaci. Doporučuje se vybrat příslušnou možnost. Důvodem je rozdílný výpočet školních průměrů. Pokud vaše škola navíc uvádí průměr předmětů na stránce deníku Vulcan, aplikace si je stáhne a tyto průměry nepočítá. To lze změnit vynucením výpočtu průměru v nastavení aplikaci.\n\nPrůměr známek pouze z vybraného semestru:\n1. Výpočet váženého průměru pro každý předmět v daném semestru\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů\n\nPrůměr průměrů z obou semestrů:\n1. Výpočet váženého průměru pro každý předmět v semestru 1 a 2\n2. Výpočet aritmetického průměru vypočítaných průměrů za semestry 1 a 2 pro každý předmět.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru sečtených průměrů\n\nPrůměr známek z celého roku:\n1. Výpočet váženého průměru za rok pro každý předmět. Konečný průměr v 1. semestru je nepodstatný.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru součtených průměrů
Jak funguje konečný průměr?
- Konečný průměr je aritmetický průměr vypočítaný ze všech aktuálně dostupných konečných známek v daném semestru.\n\nSchéma výpočtu se skládá z následujících kroků:\n1. Sčítání konečných známek zadaných učiteli\n2. Děleno počtem předmětů, pro které už byly vydány známky
+ Konečný průměr je aritmetický průměr vypočítaný ze všech aktuálně dostupných konečných známek v daném semestru.\n\nSchéma výpočtu se skládá z následujících kroků:\n1. Sčítání konečných známek zadaných učiteli\n2. Děleno počtem předmětů, pro které už byly uděleny známky
Konečný průměr
z %1$d z %2$d předmětů
Shrnutí
@@ -219,7 +220,7 @@
Čas ukončení musí být pozdější než čas zahájení
Shrnutí frekvencí
- Neprítomnosť zo školských dôvodov
+ Nepřítomnost ze školních důvodů
Omluvená nepřítomnost
Neomluvená nepřítomnost
Osvobození
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 8492f6460..86308aa19 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -71,6 +71,7 @@
Ihr Konto wiederherstellen
Wiederherstellen
Student ist bereits angemeldet
+ Standard
Kundenbetreuer
Anmelden
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 2c8a83cca..1607b17c6 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -71,6 +71,7 @@
Przywróć swoje konto
Przywróć
Uczeń jest już zalogowany
+ Standardowa
Menadżer kont
Zaloguj się
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 1ddcaf4c5..909a627cb 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -71,6 +71,7 @@
Восстановите свой аккаунт
Восстановить
Студент уже вошел в систему
+ Стандартный
Менеджер аккаунтов
Войти
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 804473add..5ebd1e76b 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -71,6 +71,7 @@
Obnovte svoj účet
Obnoviť
Žiak je už prihlásený
+ Štandardná
Manažér účtov
Prihlásiť sa
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index c53161e71..7e01f70b6 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -71,6 +71,7 @@
Відновіть свій обліковий запис
Відновити
Учень вже увійшов до системи
+ Стандартний
Менеджер аккаунтів
Увійти
From 391ee6e621e4a4d125fcf0acf0a93e79ed031e93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Wed, 6 Apr 2022 17:27:27 +0200
Subject: [PATCH 094/669] Version 1.6.1
---
app/build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 92e160106..56137fffa 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -22,8 +22,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 31
- versionCode 104
- versionName "1.6.0"
+ versionCode 105
+ versionName "1.6.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
From 1bc59cfa7f800ecadbe78cc305c9ab21cad09634 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sun, 10 Apr 2022 20:23:46 +0200
Subject: [PATCH 095/669] Another attempt to fix destination crash (#1826)
---
.../wulkanowy/ui/modules/Destination.kt | 58 +++++++++----------
.../wulkanowy/ui/modules/main/MainActivity.kt | 4 +-
.../ui/modules/main/MainPresenter.kt | 6 +-
.../NotificationsCenterFragment.kt | 2 +-
4 files changed, 35 insertions(+), 35 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
index f8c456fe6..73a680cfe 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
@@ -21,15 +21,15 @@ import kotlinx.serialization.Serializable
import java.time.LocalDate
@Serializable
-sealed class Destination private constructor() : Parcelable {
+sealed class Destination : Parcelable {
/*
Type in children classes have to be as getter to avoid null in enums
https://stackoverflow.com/questions/68866453/kotlin-enum-val-is-returning-null-despite-being-set-at-compile-time
*/
- abstract val type: Type
+ abstract val destinationType: Type
- abstract val fragment: Fragment
+ abstract val destinationFragment: Fragment
enum class Type(val defaultDestination: Destination) {
DASHBOARD(Dashboard),
@@ -50,29 +50,29 @@ sealed class Destination private constructor() : Parcelable {
@Parcelize
@Serializable
object Dashboard : Destination() {
- override val type get() = Type.DASHBOARD
- override val fragment get() = DashboardFragment.newInstance()
+ override val destinationType get() = Type.DASHBOARD
+ override val destinationFragment get() = DashboardFragment.newInstance()
}
@Parcelize
@Serializable
object Grade : Destination() {
- override val type get() = Type.GRADE
- override val fragment get() = GradeFragment.newInstance()
+ override val destinationType get() = Type.GRADE
+ override val destinationFragment get() = GradeFragment.newInstance()
}
@Parcelize
@Serializable
object Attendance : Destination() {
- override val type get() = Type.ATTENDANCE
- override val fragment get() = AttendanceFragment.newInstance()
+ override val destinationType get() = Type.ATTENDANCE
+ override val destinationFragment get() = AttendanceFragment.newInstance()
}
@Parcelize
@Serializable
object Exam : Destination() {
- override val type get() = Type.EXAM
- override val fragment get() = ExamFragment.newInstance()
+ override val destinationType get() = Type.EXAM
+ override val destinationFragment get() = ExamFragment.newInstance()
}
@Parcelize
@@ -81,63 +81,63 @@ sealed class Destination private constructor() : Parcelable {
@Serializable(with = LocalDateSerializer::class)
private val date: LocalDate? = null
) : Destination() {
- override val type get() = Type.TIMETABLE
- override val fragment get() = TimetableFragment.newInstance(date)
+ override val destinationType get() = Type.TIMETABLE
+ override val destinationFragment get() = TimetableFragment.newInstance(date)
}
@Parcelize
@Serializable
object Homework : Destination() {
- override val type get() = Type.HOMEWORK
- override val fragment get() = HomeworkFragment.newInstance()
+ override val destinationType get() = Type.HOMEWORK
+ override val destinationFragment get() = HomeworkFragment.newInstance()
}
@Parcelize
@Serializable
object Note : Destination() {
- override val type get() = Type.NOTE
- override val fragment get() = NoteFragment.newInstance()
+ override val destinationType get() = Type.NOTE
+ override val destinationFragment get() = NoteFragment.newInstance()
}
@Parcelize
@Serializable
object Conference : Destination() {
- override val type get() = Type.CONFERENCE
- override val fragment get() = ConferenceFragment.newInstance()
+ override val destinationType get() = Type.CONFERENCE
+ override val destinationFragment get() = ConferenceFragment.newInstance()
}
@Parcelize
@Serializable
object SchoolAnnouncement : Destination() {
- override val type get() = Type.SCHOOL_ANNOUNCEMENT
- override val fragment get() = SchoolAnnouncementFragment.newInstance()
+ override val destinationType get() = Type.SCHOOL_ANNOUNCEMENT
+ override val destinationFragment get() = SchoolAnnouncementFragment.newInstance()
}
@Parcelize
@Serializable
object School : Destination() {
- override val type get() = Type.SCHOOL
- override val fragment get() = SchoolFragment.newInstance()
+ override val destinationType get() = Type.SCHOOL
+ override val destinationFragment get() = SchoolFragment.newInstance()
}
@Parcelize
@Serializable
object LuckyNumber : Destination() {
- override val type get() = Type.LUCKY_NUMBER
- override val fragment get() = LuckyNumberFragment.newInstance()
+ override val destinationType get() = Type.LUCKY_NUMBER
+ override val destinationFragment get() = LuckyNumberFragment.newInstance()
}
@Parcelize
@Serializable
object More : Destination() {
- override val type get() = Type.MORE
- override val fragment get() = MoreFragment.newInstance()
+ override val destinationType get() = Type.MORE
+ override val destinationFragment get() = MoreFragment.newInstance()
}
@Parcelize
@Serializable
object Message : Destination() {
- override val type get() = Type.MESSAGE
- override val fragment get() = MessageFragment.newInstance()
+ override val destinationType get() = Type.MESSAGE
+ override val destinationFragment get() = MessageFragment.newInstance()
}
}
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 d1f324475..b6d41e2c1 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
@@ -129,7 +129,7 @@ class MainActivity : BaseActivity(), MainVie
)
}
fragmentHideStrategy = HIDE
- rootFragments = rootDestinations.map { it.fragment }
+ rootFragments = rootDestinations.map { it.destinationFragment }
initialize(startMenuIndex, savedInstanceState)
}
@@ -230,7 +230,7 @@ class MainActivity : BaseActivity(), MainVie
}
override fun openMoreDestination(destination: Destination) {
- pushView(destination.fragment)
+ pushView(destination.destinationFragment)
}
override fun notifyMenuViewReselected() {
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 e01497b9a..cb414fcbc 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
@@ -45,8 +45,8 @@ class MainPresenter @Inject constructor(
private val Destination?.startMenuIndex
get() = when {
this == null -> prefRepository.startMenuIndex
- type in rootDestinationTypeList -> {
- rootDestinationTypeList.indexOf(type)
+ destinationType in rootDestinationTypeList -> {
+ rootDestinationTypeList.indexOf(destinationType)
}
else -> 4
}
@@ -56,7 +56,7 @@ class MainPresenter @Inject constructor(
val startMenuIndex = initDestination.startMenuIndex
val destinations = rootDestinationTypeList.map {
- if (it == initDestination?.type) initDestination else it.defaultDestination
+ if (it == initDestination?.destinationType) initDestination else it.defaultDestination
}
view.initView(startMenuIndex, destinations)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt
index 4f1943f48..ca71910a4 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/notificationscenter/NotificationsCenterFragment.kt
@@ -43,7 +43,7 @@ class NotificationsCenterFragment :
override fun initView() {
notificationsCenterAdapter.onItemClickListener = { notification ->
- (requireActivity() as MainActivity).pushView(notification.destination.fragment)
+ (requireActivity() as MainActivity).pushView(notification.destination.destinationFragment)
}
with(binding.notificationsCenterRecycler) {
From e6f56a74a4360163aa5bba98bea7bc9b5b621b4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sun, 10 Apr 2022 20:37:10 +0200
Subject: [PATCH 096/669] Version 1.6.2
---
app/build.gradle | 6 +++---
app/src/main/play/release-notes/pl-PL/default.txt | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 56137fffa..2f77712fa 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -22,8 +22,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 31
- versionCode 105
- versionName "1.6.1"
+ versionCode 106
+ versionName "1.6.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -153,7 +153,7 @@ play {
defaultToAppBundles = false
track = 'production'
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
- userFraction = 0.25d
+ userFraction = 0.50d
updatePriority = 1
enabled.set(false)
}
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 f66c25495..4a3b1e2f6 100644
--- a/app/src/main/play/release-notes/pl-PL/default.txt
+++ b/app/src/main/play/release-notes/pl-PL/default.txt
@@ -1,4 +1,4 @@
-Wersja 1.6.0
+Wersja 1.6.2
- dodaliśmy możliwość usuwania wielu wiadomości jednocześnie
- dodaliśmy opcję szybkiego dodawania sprawdzianów do kalendarza
From b0885510056c8d111565b8a9fa84888e44d7c396 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 14 Apr 2022 02:04:53 +0000
Subject: [PATCH 097/669] Bump hianalytics from 6.4.1.301 to 6.4.1.302 (#1832)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 2f77712fa..ef6749a2a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
playImplementation 'com.google.android.play:core-ktx:1.8.1'
playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
- hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.301'
+ hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.302'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.5.200'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From 88576271e235444246e7a98289b2a6a2ab9248dc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 14 Apr 2022 02:05:12 +0000
Subject: [PATCH 098/669] Bump agcp from 1.6.5.200 to 1.6.5.300 (#1831)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 9aa30944f..a306227d5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
- classpath 'com.huawei.agconnect:agcp:1.6.5.200'
+ classpath 'com.huawei.agconnect:agcp:1.6.5.300'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3"
From 3caabd3e0e3a007b56d195cb49819b4b9bc58ebd Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 14 Apr 2022 02:06:04 +0000
Subject: [PATCH 099/669] Bump coroutines from 1.6.0 to 1.6.1 (#1828)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index ef6749a2a..3ec1938d8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -174,7 +174,7 @@ ext {
room = "2.4.2"
chucker = "3.5.2"
mockk = "1.12.2"
- coroutines = "1.6.0"
+ coroutines = "1.6.1"
}
dependencies {
From f912aac140442ff591ccf3fde174c6a8ce102323 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 14 Apr 2022 02:12:20 +0000
Subject: [PATCH 100/669] Bump gradle from 7.1.2 to 7.1.3 (#1829)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index a306227d5..80b2e4b74 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
- classpath 'com.android.tools.build:gradle:7.1.2'
+ classpath 'com.android.tools.build:gradle:7.1.3'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.huawei.agconnect:agcp:1.6.5.300'
From a73f39e59cbdfc0c8926e335787e16a9cffd8edc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 14 Apr 2022 02:13:09 +0000
Subject: [PATCH 101/669] Bump agconnect-crash from 1.6.5.200 to 1.6.5.300
(#1830)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 3ec1938d8..30c81b55b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -242,7 +242,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.302'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.5.200'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.5.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From 447ece3696efc0927bbdcd4c57f5282bd68bad15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sat, 16 Apr 2022 12:17:22 +0200
Subject: [PATCH 102/669] Replace destination parcelable with destination json
string (#1833)
---
.../services/shortcuts/ShortcutsHelper.kt | 58 ++++---------------
.../wulkanowy/ui/modules/Destination.kt | 17 +-----
.../wulkanowy/ui/modules/main/MainActivity.kt | 13 +++--
.../ui/modules/main/MainPresenter.kt | 7 ++-
.../ui/modules/splash/SplashActivity.kt | 12 ++--
.../ui/modules/splash/SplashPresenter.kt | 7 ++-
.../ui/modules/main/MainPresenterTest.kt | 18 +++---
.../ui/modules/splash/SplashPresenterTest.kt | 3 +-
8 files changed, 49 insertions(+), 86 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt b/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt
index ee31af46b..5e59aa54b 100644
--- a/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/shortcuts/ShortcutsHelper.kt
@@ -15,79 +15,41 @@ import javax.inject.Singleton
@Singleton
class ShortcutsHelper @Inject constructor(@ApplicationContext private val context: Context) {
- // Destination cannot be used here as shortcuts
- // require their intents to only use primitive types (see PersistableBundle.isValidType).
-
- private val destinations = mapOf(
- "grade" to Destination.Grade,
- "attendance" to Destination.Attendance,
- "exam" to Destination.Exam,
- "timetable" to Destination.Timetable()
- )
-
- init {
- initializeShortcuts()
- }
-
- fun getDestination(intent: Intent) =
- destinations[intent.getStringExtra(EXTRA_SHORTCUT_DESTINATION_ID)]
-
- private fun initializeShortcuts() {
+ fun initializeShortcuts() {
val shortcutsInfo = listOf(
ShortcutInfoCompat.Builder(context, "grade_shortcut")
.setShortLabel(context.getString(R.string.grade_title))
.setLongLabel(context.getString(R.string.grade_title))
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_grade))
- .setIntent(SplashActivity.getStartIntent(context)
- .apply {
- action = Intent.ACTION_VIEW
- putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "grade")
- }
- )
+ .setIntent(SplashActivity.getStartIntent(context, Destination.Grade)
+ .apply { action = Intent.ACTION_VIEW })
.build(),
ShortcutInfoCompat.Builder(context, "attendance_shortcut")
.setShortLabel(context.getString(R.string.attendance_title))
.setLongLabel(context.getString(R.string.attendance_title))
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_attendance))
- .setIntent(SplashActivity.getStartIntent(context)
- .apply {
- action = Intent.ACTION_VIEW
- putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "attendance")
- }
- )
+ .setIntent(SplashActivity.getStartIntent(context, Destination.Attendance)
+ .apply { action = Intent.ACTION_VIEW })
.build(),
ShortcutInfoCompat.Builder(context, "exam_shortcut")
.setShortLabel(context.getString(R.string.exam_title))
.setLongLabel(context.getString(R.string.exam_title))
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_exam))
- .setIntent(SplashActivity.getStartIntent(context)
- .apply {
- action = Intent.ACTION_VIEW
- putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "exam")
- }
- )
+ .setIntent(SplashActivity.getStartIntent(context, Destination.Exam)
+ .apply { action = Intent.ACTION_VIEW })
.build(),
ShortcutInfoCompat.Builder(context, "timetable_shortcut")
.setShortLabel(context.getString(R.string.timetable_title))
.setLongLabel(context.getString(R.string.timetable_title))
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_timetable))
- .setIntent(SplashActivity.getStartIntent(context)
- .apply {
- action = Intent.ACTION_VIEW
- putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "timetable")
- }
- )
+ .setIntent(SplashActivity.getStartIntent(context, Destination.Timetable())
+ .apply { action = Intent.ACTION_VIEW })
.build()
)
shortcutsInfo.forEach { ShortcutManagerCompat.pushDynamicShortcut(context, it) }
}
-
- private companion object {
-
- private const val EXTRA_SHORTCUT_DESTINATION_ID = "shortcut_destination_id"
- }
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
index 73a680cfe..561419a05 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/Destination.kt
@@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules
-import android.os.Parcelable
import androidx.fragment.app.Fragment
import io.github.wulkanowy.data.serializers.LocalDateSerializer
import io.github.wulkanowy.ui.modules.attendance.AttendanceFragment
@@ -16,12 +15,11 @@ import io.github.wulkanowy.ui.modules.note.NoteFragment
import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
-import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import java.time.LocalDate
@Serializable
-sealed class Destination : Parcelable {
+sealed class Destination {
/*
Type in children classes have to be as getter to avoid null in enums
@@ -47,35 +45,30 @@ sealed class Destination : Parcelable {
MESSAGE(Message);
}
- @Parcelize
@Serializable
object Dashboard : Destination() {
override val destinationType get() = Type.DASHBOARD
override val destinationFragment get() = DashboardFragment.newInstance()
}
- @Parcelize
@Serializable
object Grade : Destination() {
override val destinationType get() = Type.GRADE
override val destinationFragment get() = GradeFragment.newInstance()
}
- @Parcelize
@Serializable
object Attendance : Destination() {
override val destinationType get() = Type.ATTENDANCE
override val destinationFragment get() = AttendanceFragment.newInstance()
}
- @Parcelize
@Serializable
object Exam : Destination() {
override val destinationType get() = Type.EXAM
override val destinationFragment get() = ExamFragment.newInstance()
}
- @Parcelize
@Serializable
data class Timetable(
@Serializable(with = LocalDateSerializer::class)
@@ -85,56 +78,48 @@ sealed class Destination : Parcelable {
override val destinationFragment get() = TimetableFragment.newInstance(date)
}
- @Parcelize
@Serializable
object Homework : Destination() {
override val destinationType get() = Type.HOMEWORK
override val destinationFragment get() = HomeworkFragment.newInstance()
}
- @Parcelize
@Serializable
object Note : Destination() {
override val destinationType get() = Type.NOTE
override val destinationFragment get() = NoteFragment.newInstance()
}
- @Parcelize
@Serializable
object Conference : Destination() {
override val destinationType get() = Type.CONFERENCE
override val destinationFragment get() = ConferenceFragment.newInstance()
}
- @Parcelize
@Serializable
object SchoolAnnouncement : Destination() {
override val destinationType get() = Type.SCHOOL_ANNOUNCEMENT
override val destinationFragment get() = SchoolAnnouncementFragment.newInstance()
}
- @Parcelize
@Serializable
object School : Destination() {
override val destinationType get() = Type.SCHOOL
override val destinationFragment get() = SchoolFragment.newInstance()
}
- @Parcelize
@Serializable
object LuckyNumber : Destination() {
override val destinationType get() = Type.LUCKY_NUMBER
override val destinationFragment get() = LuckyNumberFragment.newInstance()
}
- @Parcelize
@Serializable
object More : Destination() {
override val destinationType get() = Type.MORE
override val destinationFragment get() = MoreFragment.newInstance()
}
- @Parcelize
@Serializable
object Message : Destination() {
override val destinationType get() = Type.MESSAGE
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 b6d41e2c1..1bfc8ba58 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
@@ -24,6 +24,8 @@ import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog
import io.github.wulkanowy.utils.*
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
import timber.log.Timber
import javax.inject.Inject
@@ -55,13 +57,13 @@ class MainActivity : BaseActivity(), MainVie
companion object {
- private const val EXTRA_START_DESTINATION = "start_destination"
+ private const val EXTRA_START_DESTINATION = "start_destination_json"
fun getStartIntent(
context: Context,
destination: Destination? = null,
) = Intent(context, MainActivity::class.java).apply {
- putExtra(EXTRA_START_DESTINATION, destination)
+ destination?.let { putExtra(EXTRA_START_DESTINATION, Json.encodeToString(it)) }
}
}
@@ -70,9 +72,8 @@ class MainActivity : BaseActivity(), MainVie
override val currentStackSize get() = navController.currentStack?.size
override val currentViewTitle
- get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId?.let {
- getString(it)
- }
+ get() = (navController.currentFrag as? MainView.TitledView)?.titleStringId
+ ?.let { getString(it) }
override val currentViewSubtitle get() = (navController.currentFrag as? MainView.TitledView)?.subtitleString
@@ -86,7 +87,7 @@ class MainActivity : BaseActivity(), MainVie
messageContainer = binding.mainMessageContainer
updateHelper.messageContainer = binding.mainFragmentContainer
- val destination = (intent.getParcelableExtra(EXTRA_START_DESTINATION) as Destination?)
+ val destination = intent.getStringExtra(EXTRA_START_DESTINATION)
?.takeIf { savedInstanceState == null }
presenter.onAttachView(this, destination)
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 cb414fcbc..a07bdb371 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
@@ -19,6 +19,8 @@ import io.github.wulkanowy.ui.modules.message.MessageView
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersView
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
import io.github.wulkanowy.utils.AnalyticsHelper
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
import timber.log.Timber
import java.time.Duration
import java.time.Instant
@@ -30,6 +32,7 @@ class MainPresenter @Inject constructor(
private val prefRepository: PreferencesRepository,
private val syncManager: SyncManager,
private val analytics: AnalyticsHelper,
+ private val json: Json
) : BasePresenter(errorHandler, studentRepository) {
private var studentsWitSemesters: List? = null
@@ -51,9 +54,11 @@ class MainPresenter @Inject constructor(
else -> 4
}
- fun onAttachView(view: MainView, initDestination: Destination?) {
+ fun onAttachView(view: MainView, initDestinationJson: String?) {
super.onAttachView(view)
+ val initDestination: Destination? = initDestinationJson?.let { json.decodeFromString(it) }
+
val startMenuIndex = initDestination.startMenuIndex
val destinations = rootDestinationTypeList.map {
if (it == initDestination?.destinationType) initDestination else it.defaultDestination
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt
index 24347e735..cfb628496 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashActivity.kt
@@ -15,6 +15,8 @@ import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.utils.openInternetBrowser
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
import javax.inject.Inject
@SuppressLint("CustomSplashScreen")
@@ -29,13 +31,13 @@ class SplashActivity : BaseActivity(), SplashView
companion object {
- private const val EXTRA_START_DESTINATION = "start_destination"
+ private const val EXTRA_START_DESTINATION = "start_destination_json"
private const val EXTRA_EXTERNAL_URL = "external_url"
fun getStartIntent(context: Context, destination: Destination? = null) =
Intent(context, SplashActivity::class.java).apply {
- putExtra(EXTRA_START_DESTINATION, destination)
+ destination?.let { putExtra(EXTRA_START_DESTINATION, Json.encodeToString(it)) }
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
}
@@ -43,12 +45,12 @@ class SplashActivity : BaseActivity(), SplashView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen().setKeepOnScreenCondition { true }
+ shortcutsHelper.initializeShortcuts()
val externalLink = intent?.getStringExtra(EXTRA_EXTERNAL_URL)
- val startDestination = intent?.getParcelableExtra(EXTRA_START_DESTINATION) as Destination?
- ?: shortcutsHelper.getDestination(intent)
+ val startDestinationJson = intent?.getStringExtra(EXTRA_START_DESTINATION)
- presenter.onAttachView(this, externalLink, startDestination)
+ presenter.onAttachView(this, externalLink, startDestinationJson)
}
override fun openLoginView() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt
index 0b7409020..767c885c1 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/splash/SplashPresenter.kt
@@ -5,16 +5,21 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.Destination
import kotlinx.coroutines.launch
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
import javax.inject.Inject
class SplashPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
+ private val json: Json
) : BasePresenter(errorHandler, studentRepository) {
- fun onAttachView(view: SplashView, externalUrl: String?, startDestination: Destination?) {
+ fun onAttachView(view: SplashView, externalUrl: String?, startDestinationJson: String?) {
super.onAttachView(view)
+ val startDestination: Destination? = startDestinationJson?.let { json.decodeFromString(it) }
+
if (!externalUrl.isNullOrBlank()) {
view.openExternalUrlAndFinish(externalUrl)
return
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt
index 7557d745a..720239e62 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt
@@ -5,13 +5,9 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.clearMocks
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
-import io.mockk.just
-import io.mockk.verify
+import kotlinx.serialization.json.Json
import org.junit.Before
import org.junit.Test
@@ -43,8 +39,14 @@ class MainPresenterTest {
clearMocks(mainView)
every { mainView.initView(any(), any()) } just Runs
- presenter =
- MainPresenter(errorHandler, studentRepository, prefRepository, syncManager, analytics)
+ presenter = MainPresenter(
+ errorHandler = errorHandler,
+ studentRepository = studentRepository,
+ prefRepository = prefRepository,
+ syncManager = syncManager,
+ analytics = analytics,
+ json = Json
+ )
presenter.onAttachView(mainView, null)
}
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/splash/SplashPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/splash/SplashPresenterTest.kt
index 323119742..eb362978d 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/splash/SplashPresenterTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/splash/SplashPresenterTest.kt
@@ -7,6 +7,7 @@ import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.impl.annotations.MockK
import io.mockk.verify
+import kotlinx.serialization.json.Json
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -30,7 +31,7 @@ class SplashPresenterTest {
@Before
fun setUp() {
MockKAnnotations.init(this)
- presenter = SplashPresenter(errorHandler, studentRepository)
+ presenter = SplashPresenter(errorHandler, studentRepository, Json)
}
@Test
From d37de197fca0a5f364dbcd8989056afd1b675987 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 18 Apr 2022 14:52:06 +0000
Subject: [PATCH 103/669] Bump firebase-bom from 29.3.0 to 29.3.1 (#1836)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 30c81b55b..c8a96abf2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -233,7 +233,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
- playImplementation platform('com.google.firebase:firebase-bom:29.3.0')
+ playImplementation platform('com.google.firebase:firebase-bom:29.3.1')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 426bee882c8d4aac502b99bdbee24c5ca527c2e2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Mon, 18 Apr 2022 16:52:28 +0200
Subject: [PATCH 104/669] Display timetable header as HTML on dashboard tile
(#1835)
---
.../github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt
index 3b6dc7298..9191d43c9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt
@@ -9,6 +9,7 @@ import android.os.Looper
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.text.parseAsHtml
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updateMarginsRelative
@@ -563,7 +564,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter
Date: Tue, 19 Apr 2022 09:56:01 +0200
Subject: [PATCH 105/669] Version 1.6.3
---
app/build.gradle | 4 ++--
app/src/main/play/release-notes/pl-PL/default.txt | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index c8a96abf2..188af355d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -22,8 +22,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 31
- versionCode 106
- versionName "1.6.2"
+ versionCode 107
+ versionName "1.6.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
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 4a3b1e2f6..2dba56ddd 100644
--- a/app/src/main/play/release-notes/pl-PL/default.txt
+++ b/app/src/main/play/release-notes/pl-PL/default.txt
@@ -1,4 +1,4 @@
-Wersja 1.6.2
+Wersja 1.6.3
- dodaliśmy możliwość usuwania wielu wiadomości jednocześnie
- dodaliśmy opcję szybkiego dodawania sprawdzianów do kalendarza
From 15e8e096ede8060dd0293d8b39280cbf2e03d62d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 3 May 2022 11:38:18 +0000
Subject: [PATCH 106/669] Bump robolectric from 4.7.3 to 4.8 (#1839)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 188af355d..81f7af177 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -255,7 +255,7 @@ dependencies {
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
- testImplementation 'org.robolectric:robolectric:4.7.3'
+ testImplementation 'org.robolectric:robolectric:4.8'
testImplementation "androidx.test:runner:1.4.0"
testImplementation "androidx.test.ext:junit:1.1.3"
testImplementation "androidx.test:core:1.4.0"
From 28ef8c6761fc04b2c602513a40ef27d19f555802 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 3 May 2022 11:46:03 +0000
Subject: [PATCH 107/669] Bump kotlin_version from 1.6.20 to 1.6.21 (#1837)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 80b2e4b74..2b1d16a23 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
buildscript {
ext {
- kotlin_version = '1.6.20'
+ kotlin_version = '1.6.21'
about_libraries = '8.9.4'
hilt_version = "2.41"
}
From bb052fd4c9e07ca5e55efa8f3c59758323987a2e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 12 May 2022 19:05:20 +0000
Subject: [PATCH 108/669] Bump robolectric from 4.8 to 4.8.1 (#1842)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 81f7af177..0a987cf99 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -255,7 +255,7 @@ dependencies {
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
- testImplementation 'org.robolectric:robolectric:4.8'
+ testImplementation 'org.robolectric:robolectric:4.8.1'
testImplementation "androidx.test:runner:1.4.0"
testImplementation "androidx.test.ext:junit:1.1.3"
testImplementation "androidx.test:core:1.4.0"
From 2e7caabde392f946d7b02be66f6bcf552aa5825a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 12 May 2022 19:05:40 +0000
Subject: [PATCH 109/669] Bump firebase-bom from 29.3.1 to 30.0.0 (#1844)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 0a987cf99..e91296a78 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -233,7 +233,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
- playImplementation platform('com.google.firebase:firebase-bom:29.3.1')
+ playImplementation platform('com.google.firebase:firebase-bom:30.0.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From a0bf14b5766e995110a724a580b83b717ab5fe60 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 12 May 2022 19:05:59 +0000
Subject: [PATCH 110/669] Bump agconnect-crash from 1.6.5.300 to 1.6.6.200
(#1843)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index e91296a78..2910c84b7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -242,7 +242,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.302'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.5.300'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.6.200'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From ebde42328aa096f235e6eecd9aca66beb84b847f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 12 May 2022 19:06:15 +0000
Subject: [PATCH 111/669] Bump agcp from 1.6.5.300 to 1.6.6.200 (#1845)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 2b1d16a23..862a45ccb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.1.3'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
- classpath 'com.huawei.agconnect:agcp:1.6.5.300'
+ classpath 'com.huawei.agconnect:agcp:1.6.6.200'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3"
From 445bfda801371bc852727d20f3d3e6c8504cc19c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Thu, 12 May 2022 21:07:14 +0200
Subject: [PATCH 112/669] New Crowdin updates (#1840)
---
.../main/res/values-ru/preferences_values.xml | 18 +-
app/src/main/res/values-ru/strings.xml | 546 +++++++--------
.../main/res/values-uk/preferences_values.xml | 16 +-
app/src/main/res/values-uk/strings.xml | 632 +++++++++---------
4 files changed, 606 insertions(+), 606 deletions(-)
diff --git a/app/src/main/res/values-ru/preferences_values.xml b/app/src/main/res/values-ru/preferences_values.xml
index cfdaa9577..9cc37620e 100644
--- a/app/src/main/res/values-ru/preferences_values.xml
+++ b/app/src/main/res/values-ru/preferences_values.xml
@@ -32,7 +32,7 @@
- 0,75
- - Алфавитный
+ - В алфавитном порядке
- По дате
@@ -43,22 +43,22 @@
- До 1 за раз
- Всегда развернуто
- - Неограниченные расширения
+ - Неограниченно
- - Средние оценки только с выбранного семестра
- - Средние значения для обоих семестров
- - Средняя оценок со всего года
+ - Средняя из оценок выбранного семестра
+ - Средняя из средних оценок семестров
+ - Средняя из оценок со всего года
- Счастливый номер
- - Непрочитанные сообщения
+ - Непрочитанные письма
- Посещаемость
- Уроки
- Оценки
- Домашняя работа
- - Объявления школ
- - Экзамены
- - Конференции
+ - Объявления школы
+ - Тесты
+ - Встречи
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 909a627cb..662e0934f 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -12,93 +12,93 @@
О приложении
Просмотр журнала
Отладка
- Отладка уведомления
+ Отладка уведомлений
Разработчики
Лицензии
- Сообщения
- Новое сообщение
- Новая домашняя работа
- Предупреждения и свершения
+ Письма
+ Написать
+ Новое домашнее задание
+ Замечания и свершения
Домашние задания
- Менеджер аккаунтов
- Выбор учетной записи
+ Управлять аккаунтами
+ Выбрать аккаунт
Данные аккаунта
- Информация о студенте
- Панель
+ Информация о ученике
+ Главная
Центр уведомлений
%1$d семестр, %2$d/%3$d
- Авторизируйтесь при помощи аккаунта ученика или родителя
- Введите символ со страницы регистрации: <b>%1$s</b>
- Имя пользователя
- Электронная почта
+ Воспользуйтесь аккаунтом ученика/родителя
+ Введите symbol аккаунта со страницы регистрации: <b>%1$s</b>
+ Логин
+ E-mail
Логин, PESEL или электронная почта
Пароль
- UONET + вариант регистрации
+ Тип дневника UONET+
Мобильный API
Scraper
- Гибрид
+ Hybrid
Token
PIN
Symbol
Войти
- Слишком короткий пароль
+ Пароль слишком короткий
Данные для входа указаны неверно
- %1$s. Убедитесь, что ниже выбран правильный UONET+ вариант регистра
- Неправильный PIN
+ %1$s. Убедитесь, что ниже выбран правильный тип дневника UONET+
+ Неверный PIN
Неверный token
Token просрочен
- Неверный адрес электронной почты
- Используйте назначенный логин вместо электронной почты
- Использовать назначенный логин или email в @%1$s
- Неправильный символ
- Студент не найден. Подтвердите символ и выбранный вариант регистра UONET+
+ Неверный e-mail
+ Используйте назначенный логин вместо e-mail
+ Используйте назначенный логин или email в @%1$s
+ Неверный symbol
+ Ученик не найден. Проверьте symbol и выбранный тип дненика UONET+
Данный ученик уже авторизован
- Этот символ можно найти на странице регистрации в Ученик → Телефонный доступ → Зарегистрируйте мобильное устройство.\n\nУбедитесь, что вы установили соответствующий вариант регистра в поле Разновидностью бревна UONET+ на предыдущем экране. Вулкановый на данный момент не обнаруживает дошкольников
+ Symbol можно найти на странице регистрации в Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nУбедитесь, что вы выбрали соответствующий тип дневника в поле Тип дневника UONET+ на предыдущем экране
Выберите учеников для авторизации в приложении
Другие варианты
В этом режиме не работают: счастливый номер, статистика класса по оценкам, статистика посещаемости и уроков, информация о школе и список зарегистрированных устройств
Scraper - режим, который показывает данные так же, как и сайт дневника
Hybrid - это комбинация лучших функций остальных двух режимов. Он работает быстрее, чем Scraper, и вводит функции, которых нет в режиме Mobile API. Находится в экспериментальной стадии
- Политика приватности
+ Политика конфиденциальности
Проблемы с авторизацией? Свяжитесь с нами!
- Электронная почта
+ E-mail
Discord
Отправить письмо
- Убедитесь, что вы выбрали правильный вариант регистра UONET+!
- Я забыл свой пароль
+ Убедитесь, что вы выбрали правильный тип дневника UONET+
+ Забыли пароль?
Восстановите свой аккаунт
Восстановить
- Студент уже вошел в систему
+ Ученик уже авторизован
Стандартный
Менеджер аккаунтов
Войти
Сеанс истёк
- Сеанс истёк, пожалуйста, войдите ещё раз
+ Сеанс истёк, авторизуйтесь снова
Оценка
- Семестр %d
+ %d семестр
Сменить семестр
- Оценки отсутствуют
+ Нет оценок
Стоимость
Стоимость: %s
Комментарий
Количество новых оценок: %1$d
Средняя оценка: %1$.2f
Баллы: %s
- Средняя оценка отсутствует
+ Нет средней оценки
Сумма баллов
Итоговая оценка
Ожидаемая оценка
Рассчитанная средняя оценка
- Как рассчитывается средняя работа?
- Расчетное среднее - это среднее арифметическое, рассчитанное на основе средних значений испытуемых. Это позволяет узнать приблизительное итоговое среднее значение. Он рассчитывается способом, выбранным пользователем в настройках приложения. Рекомендуется выбрать подходящий вариант. Это потому, что расчет средних показателей школы отличается. Кроме того, если ваша школа сообщает среднее значение по предметам на странице Vulcan, приложение загружает их и не вычисляет эти средние значения. Это можно изменить, принудительно вычисляя среднее значение в настройках приложения.\n\nСреднее значение только за выбранный семестр :\n1. Вычисление средневзвешенного значения по каждому предмету за семестр\n2.Добавление вычисленных средних\n3. Вычисление среднего арифметического суммарных средних\n\nСреднее из средних значений за оба семестра:\n1.Расчет средневзвешенного значения для каждого предмета в семестрах 1 и 2\n2. Вычисление среднего арифметического рассчитанных средних значений для семестров 1 и 2 по каждому предмету.\n3. Добавление вычисленных средних\n4. Расчет среднего арифметического суммированных средних\n\nСреднее значение оценок за весь год: \n1. Расчет средневзвешенного значения за год по каждому предмету. Итоговое среднее значение за 1 семестр не имеет значения.\n3. Добавление вычисленных средних\n4. Расчет среднего арифметического
- Как работает окончательный средний показатель?
- Среднее арифметическое - это среднее арифметическое, рассчитанное по всем имеющимся на данный момент итоговым классам данного семестра.\n\nСхема расчета состоит из следующих шагов:\n1. Суммирование итоговых классов преподавателей\n2. Деление по количеству уже оцененных предметов
+ Как работает \"Рассчитанная средняя оценка\"?
+ Рассчитанная средняя оценка - это среднее арифметическое, рассчитанное на основе средних оценок по предметам. Это позволяет узнать приблизительную итоговую среднюю оценку. Она рассчитывается способом, выбранным пользователем в настройках приложения. Рекомендуется выбрать подходящий вариант, так как каждая школа по разному считает среднюю оценку. Кроме того, если ваша школа выставляет средние оценки по предметам на странице Vulcan, приложение просто загрузит их. Это можно изменить, заставив приложение считать среднюю оценку в настройках.\n\nСредняя из оценок выбранного семестра:\n1. Вычисление средневзвешенного значения по каждому предмету за семестр\n2.Суммирование вычисленных значений\n3. Вычисление среднего арифметического суммированных значений\n\nСредняя из средних оценок семестров:\n1.Расчет средневзвешенного значения для каждого предмета в семестрах. \n2. Вычисление среднего арифметического из средневзвешенных значений для каждого предмета в семестрах.\n3. Суммирование средних арифметических\n4. Вычисление среднего арифматического из суммированных значений\n\nСредняя из оценок со всего года:\n1. Расчет средневзвешенного значения по каждому предмету за год. Итоговое среднее значение за 1 семестр не имеет значения.\n2. Суммирование вычисленных средних\n4. Расчет среднего арифметического суммированных чисел
+ Как работает \"Итоговая средняя оценка\"?
+ Итоговая средняя оценка - это среднее арифметическое, рассчитанное из всех имеющихся на данный момент итоговых оценок в семестре.\n\nРассчет происходит следующим образом:\n1. Суммирование итоговых оценок, выставленных преподавателями\n2. Полученная сумма делится на число предметов, по которым выставлены оценки
Итоговая средняя оценка
- от %1$d из %2$d субъектов
+ %1$d из %2$d предметов
Итоги
Класс
Пометить как \"прочитанное\"
@@ -107,10 +107,10 @@
Баллы
Легенда
Средняя класса: %1$s
- Ваша средний: %1$s
+ Ваша средняя: %1$s
Ваша оценка: %1$s
Класс
- Студент
+ Ученик
- %d оценка
- %d оценки
@@ -136,22 +136,22 @@
- Новые итоговые оценки
- - Вы получили %1$d оценку
- - Вы получили %1$d оценки
- - Вы получили %1$d оценок
- - Вы получили %1$d оценок
+ - Вы получили %1$d новую оценку
+ - Вы получили %1$d новые оценки
+ - Вы получили %1$d новых оценок
+ - Вы получили %1$d новых оценок
- - Вы получили %1$d ожидаемую оценку
- - Вы получили %1$d ожидаемые оценки
- - Вы получили %1$d ожидаемых оценок
- - Вы получили %1$d ожидаемых оценок
+ - Вы получили %1$d новую ожидаемую оценку
+ - Вы получили %1$d новые ожидаемые оценки
+ - Вы получили %1$d новых ожидаемых оценок
+ - Вы получили %1$d новых ожидаемых оценок
- - Вы получили %1$d итоговою оценку
- - Вы получили %1$d итоговых оценки
- - Вы получили %1$d итоговых оценок
- - Вы получили %1$d финальные оценки
+ - Вы получили %1$d новую итоговую оценку
+ - Вы получили %1$d новых итоговых оценки
+ - Вы получили %1$d новых итоговых оценок
+ - Вы получили %1$d новых итоговые оценки
Урок
@@ -169,36 +169,36 @@
Следующий: %s
Позже: %s
%1$s урок %2$d - %3$s
- Изменить комнату с %1$s на %2$s
- Изменить учителя с %1$s на %2$s
- Изменить тему с %1$s на %2$s
+ Аудитория изменена с %1$s на %2$s
+ Учитель изменён с %1$s на %2$s
+ Тема изменена с %1$s на %2$s
- Изменение расписания
- - Изменение расписания
- - Изменение расписания
- - Изменение расписания
+ - Изменения расписания
+ - Изменения расписания
+ - Изменения расписания
- - %1$s - %2$d изменений в расписании
- - %1$s - %2$d изменений в расписании
+ - %1$s - %2$d изменение в расписании
+ - %1$s - %2$d изменения в расписании
- %1$s - %2$d изменений в расписании
- - %1$s - %2$d изменений в расписании
+ - %1$s - %2$d изменений в расписании
- - %1$d - изменений в расписании
- - %1$d изменение в расписании
- - %1$d изменение в расписании
+ - %1$d изменение в расписании
+ - %1$d изменения в расписании
+ - %1$d изменений в расписании
- %1$d изменений в расписании
- %d изменение
- - %d изменение
- - %d изменение
+ - %d изменения
+ - %d изменений
- %d изменений
Проведённые уроки
- Просмотреть проведённые уроки
+ Показать проведённые уроки
Нет информации о проведённых уроках
Тема
Отсутствие
@@ -213,7 +213,7 @@
Дополнительный урок успешно удален
Повторять еженедельно
Удалить дополнительный урок
- Просто этот урок
+ Только этот урок
Все в серии
Время начала
Время окончания
@@ -224,35 +224,35 @@
Отсутствие по уважительной причине
Отсутствие по неуважительной причине
Освобождение
- Опоздание по уважительным причинам
- Опоздание по неуважительным причинам
+ Опоздание по уважительной причине
+ Опоздание по неуважительной причине
Присутствие
Удалено
Неизвестно
Урок №
- Данные не найдены
+ Нет записей
Причина отсутствия (необязательно)
- Послать
- Запрос на освобождение оправдания успешно отправлен!
- Выберите хотя-бы одно отсутствие
- Изменить статус
+ Отправить
+ Причина отсутствия успешно отправлена.
+ Выберите хотя бы одно отсутствие.
+ Указать причину
- - Новое посещение
- - Новое посещение
- - Новое посещение
- - Новое посещение
+ - Новая запись о посещаемости
+ - Новые записи о посещаемости
+ - Новые записи о посещаемости
+ - Новые записи о посещаемости
- - %1$d новое посещения
- - %1$d новое посещение
- - %1$d новое посещение
- - %1$d новое посещения
+ - %1$d новая запись о посещаемости
+ - %1$d новые записи о посещаемости
+ - %1$d новых записей о посещаемости
+ - %1$d новых записей о посещаемости
- - %d посещаемость
- - %d посещаемость
- - %d посещаемость
- - %d посещаемость
+ - %d запись о посещаемости
+ - %d записи о посещаемости
+ - %d записей о посещаемости
+ - %d записей о посещаемости
Общая
@@ -261,54 +261,54 @@
Тип
Дата записи
- - Новый экзамен
- - Новый экзамен
- - Новый экзамен
- - Новые экзамены
+ - Новый тест
+ - Новые тесты
+ - Новые тесты
+ - Новые тесты
- - %d новый экзамен
- - %d новый экзамен
- - %d новый экзамен
- - %d новых экзаменов
+ - %d новый тест
+ - %d новых теста
+ - %d новых тестов
+ - %d новых тестов
- - %d экзамен
- - %d экзамен
- - %d экзамен
- - %d экзаменов
+ - %d тест
+ - %d теста
+ - %d тестов
+ - %d тестов
Полученные
Отправленные
Корзина
(нет темы)
- Нет сообщений
+ Нет писем
От:
Кому:
Дата: %1$s
Ответ
Переслать
- Выбрать всё
- Снять выбор
+ Выбрать все
+ Отменить выбор
Перенести в корзину
Удалить навсегда
- Сообщение успешно удалено
+ Письмо успешно удалено
Поделиться
Печать
Тема
- Текст
- Сообщение успешно отправлено
- Сообщения не существует
+ Содержание
+ Письмо успешно отправлено
+ Письма не существует
Вы должны выбрать как минимум одного получателя
Текст сообщения должен содержать как минимум 3 знака
Только непрочитанные
Только с вложениями
- Чтение: %s
+ Прочитано: %s
Прочитано: %1$d из %2$d человек
- %1$d сообщение
- - %1$d сообщений
+ - %1$d сообщения
- %1$d сообщений
- %1$d сообщений
@@ -318,8 +318,8 @@
- Новые сообщения
- Новые сообщения
- Вы хотите восстановить черновичное сообщение?
- Вы хотите восстановить черновик сообщения с получателями: %s?
+ Вы хотите восстановить черновик письма?
+ Вы хотите восстановить черновик письма с получателями: %s?
- Вы получили %1$d новое сообщение
- Вы получили %1$d новых сообщения
@@ -334,7 +334,7 @@
Сообщение удалено
- Нет информации о заметках
+ Нет записей о замечаниях и свершениях
Баллы
- %d предупреждение
@@ -345,33 +345,33 @@
- Новое предупреждение
- Новые предупреждения
- - Новых предупреждений
- - Новых предупреждений
+ - Новые предупреждения
+ - Новые предупреждения
- - Вы получили %1$d предупреждение
- - Вы получили %1$d предупреждения
- - Вы получили %1$d предупреждений
- - Вы получили %1$d предупреждений
+ - Вы получили %1$d новое предупреждение
+ - Вы получили %1$d новые предупреждения
+ - Вы получили %1$d новых предупреждений
+ - Вы получили %1$d новых предупреждений
- %d похвала
- - %d похвала
- - %d похвала
- - %d похвала
+ - %d похвалы
+ - %d похвал
+ - %d похвал
- Новая похвала
- Новые похвалы
- - Новые свершения
+ - Новые похвалы
- Новые похвалы
- - Вы получили %1$d похвалу
- - Вы получили %1$d похвалы
- - Вы получили %1$d похвалы
- - Вы получили %1$d похвалы
+ - Вы получили %1$d новую похвалу
+ - Вы получили %1$d новые похвалы
+ - Вы получили %1$d новых похвал
+ - Вы получили %1$d новых похвал
@@ -387,34 +387,34 @@
- Новые нейтральные замечания
- - Вы получили %1$d нейтральное замечание
- - Вы получили %1$d нейтральных замечания
- - Вы получили %1$d нейтральных замечаний
- - Вы получили %1$d нейтральных замечаний
+ - Вы получили %1$d новое нейтральное замечание
+ - Вы получили %1$d новые нейтральных замечания
+ - Вы получили %1$d новых нейтральных замечаний
+ - Вы получили %1$d новых нейтральных замечаний
Нет домашних заданий
Завершено
Не завершено
- Добавить домашнюю работу
- Домашняя работа успешно добавлена
- Домашняя работа успешно удалена
+ Добавить домашнее задание
+ Домашнее задание успешно добавлена
+ Домашнее задание успешно удалена
Вложения
- - Новая домашняя работа
- - Новая домашняя работа
- - Новая домашняя работа
- - Новая домашняя работа
+ - Новое домашнее задание
+ - Новые домашние работы
+ - Новые домашние задания
+ - Новые домашние задания
- - Вы получили %d новых домашних заданий
+ - Вы получили %d новое домашнее задание
- Вы получили %d новых домашних заданий
- Вы получили %d новых домашних заданий
- Вы получили %d новых домашних заданий
- - %d домашних заданий
- - %d домашних заданий
+ - %d домашнее задание
+ - %d домашних задания
- %d домашних заданий
- %d домашних заданий
@@ -423,15 +423,15 @@
Сегодняшний счастливый номер это
Нет данных о счастливом номере
Сегодняшний счастливый номер
- Сегодняшний номер удачи: %s
+ Сегодняшний счастливый номер: %s
Показать историю
- История удачных чисел
- Нет информации о номерах удачи
+ История счастливых номеров
+ Нет информации о счастливых номерах
Мобильные устройства
Нет устройств
- Отменить регистрацию
+ Удалить
Устройство удалено
QR-код
Token
@@ -457,45 +457,45 @@
Встречи
Нет информации о встречах
- - %d конференция
- - %d конференция
- - %d конференция
- - %d конференций
+ - %d встреча
+ - %d встречи
+ - %d встреч
+ - %d встреч
- - Новая конференция
- - Новая конференция
- - Новая конференция
- - Новые конференции
+ - Новая встреча
+ - Новые встречи
+ - Новые встречи
+ - Новые встречи
- - У вас %1$d новая конференция
- - У вас %1$d новая конференция
- - У вас %1$d новая конференция
- - У вас %1$d новых конференций
+ - У вас %1$d новая встреча
+ - У вас %1$d новые встречи
+ - У вас %1$d новых встреч
+ - У вас %1$d новых встреч
- Присутствует на конференции
+ Присутствует на встрече
Повестка дня
- Объявления школ
- Нет объявлений о школе
+ Объявления школы
+ Нет школьных объявлений
- - Объявление о школе %d
- - Объявление о школе %d
- - Объявление о школе %d
- - Объявления школы %d
+ - %d школьное объявление
+ - %d школьных объявления
+ - %d школьных объявлений
+ - %d школьных объявлений
- - Объявление о новой школе
- - Объявление о новой школе
- - Объявление о новой школе
- - Объявления о новых школах
+ - Новое школьное объявление
+ - Новые школьные объявления
+ - Новые школьные объявления
+ - Новые школьные объявления
- - У вас %1$d объявление о новой школе
- - У вас %1$d объявление о новой школе
- - У тебя %1$d новых школьных объявлений
- - У тебя %1$d новых школьных объявлений
+ - У вас %1$d новое школьное объявление
+ - У вас %1$d новые школьных объявления
+ - У вас %1$d новых школьных объявлений
+ - У вас %1$d новых школьных объявлений
Добавить аккаунт
@@ -505,29 +505,29 @@
Профиль ученика
Профиль родителя
Изменить данные
- Менеджер аккаунтов
- Выберите Студента
+ Управлять аккаунтами
+ Выбрать ученика
Семья
- Контакт
- Детали проживания
- Персональные данные
+ Контакты
+ Места жительства
+ Личная информация
Версия приложения
Разработчики
- Список разработчиков \"Вулкановый\"
+ Список разработчиков приложения
Возникла ошибка?
Сообщить о ошибке
FAQ
Часто задаваемые вопросы
Сервер Discord
Присоединиться к сообществу приложения
- Facebook фан-страница
- Странница в твиттере
- Подпишись на нас в твиттере
- Поставьте лайк на нашей странице в Facebook
- Политика приватности
+ Страница в Facebook
+ Страница в Twitter
+ Следите за нами в Twitter
+ Лайкните нас в Facebook
+ Политика конфиденциальности
Правила хранения личных данных
- Параметры системы
+ Системные настройки
Открыть системные настройки
Домашняя страница
Помочь в развитии приложения
@@ -541,23 +541,23 @@
Нет информации об ученике или семье ученика
Имя
- Фамилия
+ Второе имя
Пол
Польское гражданство
- Фамилия
+ Девичья фамилия
Имена матери и отца
- Телефон
+ Домашний телефон
Сотовый телефон
- Эл. почта
+ E-mail
Адрес проживания
- Адрес регистрации
- Адрес переписки
+ Адрес прописки
+ Почтовый адрес
Фамилия и имя
Степень родства
Адрес
- Телефоны
- Муж
- Женская
+ Номера телефонов
+ Мужской
+ Женский
Фамилия
Опекун
@@ -565,7 +565,7 @@
Добавить ник
Выберите цвет аватара
- Поделиться логами
+ Отправить логи
Обновить
Уроки
@@ -579,20 +579,20 @@
Далее:
Позднее:
- - Еще %1$d урок
- - Еще %1$d урок
- - Еще %1$d урок
- - Ещё %1$d уроков
+ - ещё %1$d урок
+ - ещё %1$d урока
+ - ещё %1$d уроков
+ - ещё %1$d уроков
до %1$s
Нет предстоящих занятий
Произошла ошибка при загрузке уроков
- Домашняя работа
+ Домашнее задание
Нет домашних заданий
Произошла ошибка при загрузке домашнего задания
- - Ещё %1$d домашних заданий
- - Ещё %1$d домашних заданий
+ - Ещё %1$d домашнее задание
+ - Ещё %1$d домашних задания
- Ещё %1$d домашних заданий
- Ещё %1$d домашних заданий
@@ -600,32 +600,32 @@
Последние оценки
Нет новых оценок
Произошла ошибка при загрузке оценок
- Объявления школ
- Нет текущих объявлений
+ Школьные объявления
+ Нет объявлений
Произошла ошибка при загрузке объявлений
- - Ещё %1$d объявление
- - Ещё %1$d объявление
- - Ещё %1$d объявление
- - Ещё %1$d объявлений
+ - ещё %1$d объявление
+ - ещё %1$d объявления
+ - ещё %1$d объявлений
+ - ещё %1$d объявлений
- Экзамены
- Нет предстоящих экзаменов
- Произошла ошибка при загрузке экзамена
+ Тесты
+ Нет предстоящих тестов
+ Произошла ошибка при загрузке тестов
- - Еще %1$d экзамен
- - Еще %1$d экзамен
- - Еще %1$d экзамен
- - ещё %1$d экзаменов
+ - ещё %1$d тест
+ - ещё %1$d теста
+ - ещё %1$d тестов
+ - ещё %1$d тестов
- Конференции
- Нет предстоящих конференций
- Произошла ошибка при загрузке конференций
+ Встречи
+ Нет предстоящих встреч
+ Произошла ошибка при загрузке встреч
- - Еще %1$d конференция
- - Еще %1$d конференция
- - Еще %1$d конференция
- - Еще %1$d конференций
+ - еще %1$d встреча
+ - еще %1$d встречи
+ - еще %1$d встреч
+ - еще %1$d встреч
Произошла ошибка при загрузке данных
Отсутствует
@@ -668,96 +668,96 @@
Приложение
Окно по умолчанию
- Рассчитанные средние параметры
+ Параметры расчёта средних оценок
Принудительно высчитать среднюю оценку через приложение
- Показать присутствие
+ Показывать присутствия
Тема
- Расширяется оценка
+ Разворачивание оценок
Отметить текущий урок
Показать группы рядом с темами
Показывать диаграммы в оценках класса
Показать предметы без оценок
- Схема цветов оценок
+ Цветовая схема оценок
Сортировка уроков
Язык
Уведомления
Прочее
Показывать уведомления
- Показывать уведомления о будущих уроках
- Сделать уведомления о предстоящем уроке постоянным
- Выключить, когда уведомление не отображается в чата/полосе
- Открыть настройки уведомлений системы
+ Показывать уведомления о предстоящих уроках
+ Сделать уведомление о предстоящем уроке постоянным
+ Выключите, если уведомление не отображается на умных часах
+ Открыть системные настройки уведомлений
Исправить проблемы с синхронизацией и уведомлениями
На вашем устройстве могут быть проблемы с синхронизацией данных и уведомлениями.\n\nЧтобы их исправить, вам необходимо добавить Wulkanowy в авто-старт и выключить оптимизацию/экономию батареи в настройках устройства.
- Показывать дебаг-уведомления
+ Показывать отладочные уведомления
Синхронизация отключена
- Официальные уведомления приложения
- Записывать официальные уведомления
+ Уведомления официального приложения
+ Захватывать уведомления официального приложения
Удалить уведомления от официального приложения после захвата
- Показывать push-уведомления
- С помощью этой функции вы можете получить замену push-уведомлений, как в официальном приложении. Все, что вам нужно сделать, это разрешить Wulkanowy получать все уведомления в настройках системы.\n\nКак это работает?\nКогда вы получаете уведомление в Dziennik VULCAN, Wulkanowy будет уведомлен (это требует дополнительных прав) и запустит синхронизацию, чтобы отправить свое уведомление.\n\nТОЛЬКО ДЛЯ ПОЛЬЗОВАТЕЛЯ
- Показывать уведомления о будущих уроках
- Вы должны разрешить приложению Wulkanowy установить будильник и напоминания в настройках системы, чтобы использовать эту функцию.
+ Захват уведомлений
+ С помощью этой функции вы можете получить аналог push-уведомлений из официального приложения. Все, что вам нужно сделать, это разрешить Wulkanowy получать все уведомления в настройках системы.\n\nКак это работает?\nКогда вы получаете уведомление в Dziennik VULCAN, Wulkanowy будет уведомлен (для этого и нужны дополнительные разрешения) и запустит синхронизацию, чтобы отправить свое уведомление.\n\nТОЛЬКО ДЛЯ ПРОДВИНУТЫХ ПОЛЬЗОВАТЕЛЕЙ
+ Показывать уведомления о предстоящих уроках
+ Вы должны разрешить Wulkanowy устанавливать будильник и напоминания в настройках системы, чтобы использовать эту функцию.
Перейти к настройкам
Синхронизация
Автоматическая синхронизация
- Приостановить синхронизации во время каникул
+ Приостановлена во время каникул
Интервал синхронизации
Только через Wi-Fi
Синхронизировать
Синхронизировано!
Синхронизация не удалась
- Идёт синхронизация
+ Синхронизация...
Последняя полная синхронизация: %s
Стоимость плюса
Стоимость минуса
Отвечать с историей сообщений
- Показывать среднее арифметическое при отсутствии весов
+ Показывать среднее арифметическое при отсутствии стоимости
Поддержка
- Смотреть одиночную рекламу для поддержки проекта
+ Посмотреть рекламу для поддержки проекта
Согласие на обработку данных
Для просмотра рекламы вы должны согласиться с условиями обработки данных нашей Политики конфиденциальности
- Согласен
+ Согласиться
Политика конфиденциальности
- Объявление загружается
+ Реклама загружается
Спасибо за вашу поддержку, возвращайтесь позже для дополнительной рекламы
Расширенные
- Внешний вид & Поведение
+ Внешний вид и поведение
Уведомления
Синхронизация
Реклама
Оценки
- Панель
+ Главная
Видимость плиток
Посещаемость
Расписание
Оценки
- Расчетное среднее
+ Рассчитанная средняя оценка
Сообщения
- Внешний вид & Поведение
- Языки, темы, темы сортировки темы
- Уведомления приложений, проблемы с устранением
+ Внешний вид и поведение
+ Язык, тема, сортировка предметов
+ Настройки уведомлений приложения
Уведомления
Синхронизация
- Автоматическое обновление, интервал синхронизации
- Значения плюс и минус, средний расчет
+ Автоматическая синхронизация и её интервал
+ Значения плюса и минуса, расчёт средней оценки
Расширенные
- Версия приложения, участники, социальные порталы
- Отображение объявлений, поддержка проекта
+ Версия приложения, разработчики, соц. сети
+ Посмотреть рекламу, чтобы поддержать преокт
Новые оценки
- Новая домашняя работа
- Новые конференции
- Новые экзамены
+ Новое домашнее задание
+ Новые встречи
+ Новые тесты
Счастливый номер
- Новые сообщения
- Новые заметки
- Объявления о новых школах
- Показывать push-уведомления
- Будущие уроки
- Дебаг
- Изменение расписания
- Новое посещение
+ Новые письма
+ Новые замечания
+ Новые школьные объявления
+ Push-уведомления
+ Предстоящие уроки
+ Отладка
+ Изменения в расписании
+ Новая запись о посещаемости
Чёрный
Красный
@@ -771,16 +771,16 @@
Перезапустить
Не удалось обновить! Wulkanowy может работать некорректно. Рассмотрите возможность обновления
- Нет интернет-подключения
- Произошла ошибка. Проверьте часы вашего устройства
- Не удалось подключиться к регистрации. Серверы могут быть перегружены. Пожалуйста, повторите попытку позже
- Не удалось загрузить данные. Пожалуйста, повторите попытку позже
- Необходимо изменить пароль реестра
- Технический перерыв в журнале UONET + продолжается. Попробуйте позже
- Неизвестная ошибка UONET + регистр. Попробуйте позже
- Неизвестная ошибка приложения. Пожалуйста, повторите попытку позже
- Произошла неожиданная ошибка
- Функция была выключена школой
- Функция не доступна в этом режиме
- Это поле является обязательным
+ Интернет-соединение отсутствует
+ Произошла ошибка. Проверьте время на вашем устройстве
+ Не удалось подключиться к дневнику. Возможно, сервера перегружены, повторите попытку позже
+ Не удалось загрузить данные, повторите попытку позже
+ Необходимо изменить пароль дневника
+ UONET+ проводит техническое обслуживание, повторите попытку позже
+ Неизвестная ошибка дневника UONET+, повторите попытку позже
+ Неизвестная ошибка приложения, повторите попытку позже
+ Произошла непредвиденная ошибка
+ Функция отключена вашей школой
+ Функция недоступна в режиме Mobile API. Воспользуйтесь другим режимом
+ Это поле обязательно
diff --git a/app/src/main/res/values-uk/preferences_values.xml b/app/src/main/res/values-uk/preferences_values.xml
index a8c09bcf8..82c5b6ecc 100644
--- a/app/src/main/res/values-uk/preferences_values.xml
+++ b/app/src/main/res/values-uk/preferences_values.xml
@@ -41,24 +41,24 @@
- Кольори оцінок в щоденнику
- - Раз до 1
- - Завжди розгорнутий
- - Необмежена кількість розширень
+ - До 1 за раз
+ - Завжди розгорнуті
+ - Необмежена кількість розгортань
- - Середні оцінки тільки від обраного семестру
- - Середнє значення для обох семестів
- - Середнє оцінювання за весь рік
+ - Середні оцінки тільки за обраний семестр
+ - Середнє значення з обох семестрів
+ - Середня оцінка з цілого року
- Щасливий номер
- - Непрочитані повідомлення
+ - Непрочитані листи
- Відвідуваність
- Уроки
- Оцінки
- Домашні завдання
- Оголошення школи
- Тести
- - Конференції
+ - Зустрічі
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 7e01f70b6..0cc50dbe5 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -8,34 +8,34 @@
Тести
Розклад
Налаштування
- Інше
- Про додаток
- Переглядач журналів
+ Більше
+ Про
+ Переглядач логів
Відладка
- Налагодження сповіщень
+ Відладка сповіщень
Розробники
Ліцензії
- Повідомлення
- Нове повідомлення
- Нова домашня робота
- Нотатки та досягнення
+ Листи
+ Новий лист
+ Нове домашнє завдання
+ Зауваження та похвали
Домашні завдання
- Менеджер аккаунтів
- Виберіть обліковий запис
+ Змінити облікові записи
+ Вибрати обліковий запис
Деталі облікового запису
Інформація про учня
- Дошка
- Журнал сповіщень
+ Головна
+ Центр сповіщень
%1$d семестр, %2$d/%3$d
- Авторизуйтеся за допомогою аккаунта учня або батьків
- Введіть символ зі сторінки реєстру: <b>%1$s</b>
+ Авторизуйтеся за допомогою облікового запису учня або батьків
+ Введіть symbol зі сторінки щоденника: <b>%1$s</b>
Ім\'я користувача
- Електронна пошта
- Логін, PESEL або електронна пошта
+ E-mail
+ Логін, PESEL або e-mail
Пароль
- UONET + варіант реєстрації
+ Тип щоденника UONET+
Мobile API
Scraper
Hybrid
@@ -45,35 +45,35 @@
Увійти
Занадто короткий пароль
Вказані невірні дані
- %1$s. Переконайтеся, що обрано правильну варіацію запису UONET+
+ %1$s. Переконайтеся, що обрано правильний тип щоденника UONET+
Неправильний PIN
Неправильний token
- Минув термін дії токену
- Недійсна адреса електронної пошти
- Використовуйте призначений логін замість електронної пошти
- Використовуйте призначений логін або електронну адресу в @%1$s
- Неправильний симбвол
- Студента не знайдено Перевірте символ та обраний варіант реєстру UONET+
- Даного учня вже авторизовано
- Символ можна знайти на сторінці реєстрації в Учень → Мобільний доступ → Додайте мобільне приладдя .\n\nПереконайтесь, що ви встановили відповідний варіант реєстру в полі UONET + варіант реєстрації на попередньому екрані. На даний момент Wulkanowy не виявляє учнів дошкільних закладів
+ Token згаснув
+ Недійсна адреса e-mail
+ Використовуйте призначений логін замість адреси e-mail
+ Використовуйте призначений логін або адресу e-mail в @%1$s
+ Неправильний symbol
+ Студента не знайдено. Перевірте symbol та обранний тип щоденника UONET+
+ Цього учня вже авторизовано
+ Symbol можна знайти на сторінці реєстрації в Uczeń→ Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nПереконайтесь, що ви встановили відповідний тип щоденника в полі Тип щоденника UONET+ на попередньому екрані
Виберіть учнів для авторизації в додатку
Інші варіанти
- У цьому режимі не працюють: щасливий номер, статистика класу по оцінкам, статистика відвідуваності і уроків, інформація про школу і список зареєстрованних пристроїв
+ У цьому режимі не працюють: щасливий номер, статистика класу по оцінкам, статистика відвідуваності та уроків, інформація про школу та список зареєстрованих пристроїв
Scraper - режим, котрий показує дані так само, як і сторінка щоденника
- Hybrid - це комбінація найкращих функцій інших двох режимів. Він працює швидше, ніж Scraper, и впроваджує функції, котрих нема в режимі Mobile API. Знаходиться в експериментальній стадії
- Політика приватності
+ Hybrid - це комбінація найкращих функцій інших двох режимів. Він працює швидше, ніж Scraper, й впроваджує функції, котрих нема в режимі Mobile API. Знаходиться в експериментальній стадії
+ Політика конфіденційності
Проблеми з авторизацією? Зв\'яжіться з нами!
- Електронна пошта
+ E-mail
Discord
- Відправити лист
- Переконайтеся, що ви вибрали правильний варіант реєстрації UONET+!
+ Надіслати електронний лист
+ Переконайтеся, що ви вибрали правильний тип щоденника UONET+!
Забули пароль?
Відновіть свій обліковий запис
Відновити
- Учень вже увійшов до системи
+ Учня вже авторизовано
Стандартний
- Менеджер аккаунтів
+ Змінити облікові записи
Увійти
Минув термін дії сесії
Минув термін дії сесії, авторизуйтеся знову
@@ -87,26 +87,26 @@
Коментар
Кількість нових оцінок: %1$d
Середня оцінка: %1$.2f
- Балів: %s
- Відсутність середньої оцінки
- Сума балів
+ Бали: %s
+ Середня оцінка відсутня
+ Всього балів
Підсумкова оцінка
- Очікувана оцінка
+ Передбачувана оцінка
Розрахована середня оцінка
- Як розраховується середньо?
- Розрахункове середнє - це середнє арифметичне, обчислене з середніх показників для випробуваних. Це дозволяє дізнатися приблизну кінцеву середню величину. Він розраховується способом, обраним користувачем у налаштуваннях програми. Рекомендується вибрати відповідний варіант. Це пояснюється тим, що розрахунок середніх показників за школою відрізняється. Крім того, якщо у вашій школі повідомляється середнє значення предметів на сторінці Вулкан, програма завантажує їх і не обчислює ці середні значення. Це можна змінити шляхом примусового розрахунку середнього значення в налаштуваннях програми.\n\nСередні оцінки лише за вибраний семестр :\n1. Розрахунок середньозваженого для кожного предмета в даному семестрі\n2.Додавання розрахункових середніх\n3. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення середніх показників за обидва семестри :\n1.Обчислення середньозваженого значення для кожного предмета у 1 та 2 семестрах\n2. Обчислення середнього арифметичного розрахункових середніх показників за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахункових середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого показника за рік для кожного предмета. Остаточний середній показник у 1 -му семестрі не має значення.\n3. Додавання розрахункових середніх \n4. Обчислення середнього арифметичного середніх суммованих середніх
- Як працює кінцевий середній показник?
- Підсумкове середнє значення - це середнє арифметичне, обчислене з усіх наявних наразі підсумкових оцінок у даному семестрі. \ N \ nСхема обчислення складається з таких кроків: \ n1. Підбиття підсумкових оцінок викладачів \ n2. Поділіть на кількість предметів, які вже оцінені
+ Як працює \"Розрахована середня оцінка\"?
+ Розрахована середня оцінка - це середнє арифметичне, обчислене з середніх оцінок з предметів.Це дозволяє дізнатися приблизну кінцеву середню оцінку.Вона розраховується способом, обраним користувачем у налаштуваннях програми.Рекомендується вибрати відповідний варіант, тому що кожна школа по різному розраховує середню оцінку.Крім того, якщо у вашій школі повідомляється середня оцінка з предметів на сторінці Vulcan, програма тільки завантажує ці оцінки.Це можна змінити шляхом примусового розрахунку середньоЇ оцінки в налаштуваннях програми.\n\nСередні оцінки тільки за обраний семестр:\n1. Розрахунок середньозваженого числа для кожного предмета в даному семестрі\n2. Сумування розрахованих числ\n3. Розрахунок середнього арифметичного з сумованих чисел\n\nСереднє значення з обох семестрів:\n1. Обчислення середньозваженого числа для кожного предмета у 1 та 2 семестрі\n2. Обчислення середнього арифметичного з розрахованих середньозважених числ за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахованих середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого числа за рік для кожного предмета. Підсумковий середній показник у 1-му семестрі не має значення.\n2. Сумування розрахованих середніх\n3. Обчислення середнього арифметичного з суммованих середніх
+ Як працює \"Підсумкова середня оцінка\"?
+ Підсумкове середнє значення - це середнє арифметичне, обчислене з усіх наявних наразі підсумкових оцінок у даному семестрі. \n\nСхема обчислення складається з таких кроків:\n1. Сумування підсумкових оцінок, виставленних викладачами\n2. Ділення на кількість предметів, з яких виставлені ці оцінки
Підсумкова середня оцінка
- з %1$d із %2$d тем
+ %1$d із %2$d предметів
Підсумок
Клас
Позначити як прочитане
- Поточні
+ Часткові
Семестрові
Бали
Умовні позначення
- Середня классу: %1$s
+ Середня класу: %1$s
Ваша середня: %1$s
Ваша оцінка: %1$s
Клас
@@ -124,10 +124,10 @@
- Нові оцінки
- - Нова прогнозована оцінка
- - Нові прогнозовані оцінки
- - Нові прогнозовані оцінки
- - Нові прогнозовані оцінки
+ - Нова передбачувана оцінка
+ - Нові передбачувані оцінки
+ - Нові передбачувані оцінки
+ - Нові передбачувані оцінки
- Нова підсумкова оцінка
@@ -136,16 +136,16 @@
- Нові підсумкові оцінки
- - Ви отримали %1$d оцінку
- - Ви отримали %1$d оцінки
- - Ви отримали %1$d оцінок
- - Ви отримали %1$d оцінок
+ - Ви отримали %1$d нову оцінку
+ - Ви отримали %1$d нові оцінки
+ - Ви отримали %1$d нових оцінок
+ - Ви отримали %1$d нових оцінок
- - Ви отримали %1$d нову прогнозовану оцінку
- - Ви отримали %1$d нові прогнозовані оцінки
- - Ви отримали %1$d нових прогнозованих оцінок
- - Ви отримали %1$d нових прогнозованих оцінок
+ - Ви отримали %1$d нову передбачувану оцінку
+ - Ви отримали %1$d нові передбачувані оцінкі
+ - Ви отримали %1$d нових передбачуваних оцінок
+ - Ви отримали %1$d нових передбачуваних оцінок
- Ви отримали %1$d нову підсумкову оцінку
@@ -159,62 +159,62 @@
Група
Години
Зміни
- Брак уроків у цей день
+ Немає уроків у цей день
%s хвилин
%s сек
- %1$s лишилося
+ %1$s залишилося
через %1$s
- Завершено
+ Закінчено
Зараз: %s
Наступний: %s
Пізніше: %s
%1$s урок %2$d - %3$s
- Зміна місця з %1$s на %2$s
- Змінити вчителя з %1$s на %2$s
+ Зміна аудіторії з %1$s на %2$s
+ Зміна вчителя з %1$s на %2$s
Зміна теми з %1$s на %2$s
- Зміна у розкладі
- - Зміна у розкладі
- - Зміна у розкладі
- - Зміни у розкладі рейсу
+ - Зміни у розкладі
+ - Зміни у розкладі
+ - Зміни у розкладі
- %1$s - %2$d зміна в розкладі
- - %1$s - %2$d зміна в розкладі
- - %1$s - %2$d зміна в розкладі
- - %1$s - %2$d змін у розкладі
+ - %1$s - %2$d зміни в розкладі
+ - %1$s - %2$d змін в розкладі
+ - %1$s - %2$d змін у розкладі
- %1$d зміна в розкладі
- %1$d зміна в розкладі
- %1$d зміна в розкладі
- - %1$d змін у розкладі
+ - %1$d змін в розкладі
- %d зміна
- - %d зміна
- - %d зміна
+ - %d зміни
+ - %d змін
- %d змін
Уроки, що відбулися
- Показати уроки, що відбулися
- Брак інформації о уроках, що відбулися
+ Показати завершені уроки
+ Немає інформації про завершенні уроки
Тема
Відсутність
Ресурси
Додаткові уроки
Показати додаткові уроки
- Немає інформації про додаткових уроків
+ Немає інформації про додаткові уроки
Новий урок
Новий додатковий урок
Додатковий урок успішно додано
- Успішно видалено додаткове заняття
+ Успішно видалено додатковий урок
Повторювати щотижня
Видалити додатковий урок
Тільки цей урок
- Все в серії
+ Всі в серії
Час початку
Час завершення
Час завершення має бути більшим, ніж час початку
@@ -222,10 +222,10 @@
Підсумок відвідуваності
Відсутність зі шкільних причин
Відсутність з поважних причин
- Відсутність з не поважних причин
+ Відсутність без поважних причин
Звільнення
Спізнення з поважних причин
- Спізнення з не поважних причин
+ Спізнення без поважних причин
Присутність
Вилучено
Невідомо
@@ -237,22 +237,22 @@
Оберіть хоча б одну відсутність
Змінити статус
- - Нова відвідуваність
- - Нова відвідуваність
- - Нова відвідуваність
- - Нова відвідуваність
+ - Новий запис відвідуваності
+ - Нові записи відвідуваності
+ - Нові записи відвідуваності
+ - Нові записи відвідуваності
- - %1$d новий відвідувач
- - %1$d новий відвідувач
- - %1$d новий відвідувач
- - %1$d відвідування
+ - %1$d новий запис відвідуваності
+ - %1$d нові записи відвідуваності
+ - %1$d нові записи відвідуваності
+ - %1$d нові записи відвідуваності
- - %d відвідування
- - %d відвідування
- - %d відвідування
- - %d відвідування
+ - %d запис відвідуваності
+ - %d записи відвідуваності
+ - %d записів відвідуваності
+ - %d записів відвідуваності
Загальна
@@ -261,80 +261,80 @@
Тип
Дата запису
- - Новий іспит
- - Новий іспит
- - Новий іспит
- - Нові іспити
+ - Новий тест
+ - Нові тести
+ - Нові тести
+ - Нові тести
- - %d новий екзамен
- - %d новий екзамен
- - %d новий екзамен
- - %d нових іспитів
+ - %d новий тест
+ - %d нових тести
+ - %d нових тестів
+ - %d нових тестів
- - %d екзамен
- - %d екзамен
- - %d екзамен
- - %d іспити
+ - %d тест
+ - %d тести
+ - %d тести
+ - %d тести
Отримані
Відправлені
Кошик
(брак теми)
- Нема повідомлень
+ Немає листів
Від:
Кому:
Дата: %1$s
Відповісти
Переслати
- Вибрати все
+ Вибрати всі
Відмінити вибір
Перемістити до кошика
Видалити назавжди
- Повідомлення було успішно видалено
- Поділіться
+ Лист було успішно видалено
+ Поділитись
Друк
Тема
Зміст
- Повідомлення було успішно відправлено
- Такого повідомлення не існує
+ Лист було успішно відправлено
+ Такого листа не існує
Необхідно обрати принаймні 1 адресата
- Зміст повідомлення мусить складатися принаймні з 3 знаків
+ Зміст листа повинен складатися принаймні з 3 знаків
Лише непрочитані
Тільки з вкладеннями
- Читання: %s
- Прочитанно:%1$d через %2$d людей
+ Прочитаний: %s
+ Прочитаний: %1$d з %2$d осіб
- - %1$d повідомлення
- - %1$d повідомлень
- - %1$d повідомлень
- - %1$d повідомлень
+ - %1$d лист
+ - %1$d листи
+ - %1$d листів
+ - %1$d листів
- - Нове повідомлення
- - Нові повідомлення
- - Нові повідомлення
- - Нові повідомлення
+ - Новий лист
+ - Нові листи
+ - Нові листи
+ - Нові листи
- Ви хочете відновити повідомлення в чернетці?
- Ви хочете відновити чернетку повідомлення з отримувачами: %s?
+ Ви хочете відновити чорновик листа?
+ Ви хочете відновити чорновик листа з отримувачами: %s?
- - Ви отримали %1$d нове повідомлення
- - Ви отримали %1$d нових повідомлення
- - Ви отримали %1$d нових повідомлень
- - Ви отримали %1$d нових повідомлень
+ - Ви отримали %1$d новий лист
+ - Ви отримали %1$d нові листи
+ - Ви отримали %1$d нових листів
+ - Ви отримали %1$d нових листів
- %1$d вибрано
- - вибрано %1$d
- - вибрано %1$d
+ - %1$d вибрано
+ - %1$d вибрано
- %1$d вибрано
- Повідомлення видалені
+ Листи видалені
- Брак інформації о зауваженнях
+ Немає інформації о зауваженнях
Бали
- %d зауваження
@@ -343,20 +343,20 @@
- %d зауважень
- - Нова нотатка
- - Нові нотатки
- - Нових нотаток
- - Нових нотаток
+ - Нове зауваження
+ - Нові зауваження
+ - Нові зауваження
+ - Нові зауваження
- - %1$d нове зауваження
- - %1$d нових зауваження
- - %1$d нових зауважень
- - %1$d нових зауважень
+ - Ви отримали %1$d нове зауваження
+ - Ви отримали %1$d нові зауваження
+ - Ви отримали %1$d нових зауважень
+ - Ви отримали %1$d нових зауважень
- - %d похвалили
+ - %d похвал
- %d похвали
- %d похвал
- %d похвал
@@ -375,62 +375,62 @@
- - %d нейтральна нота
- - %d нейтральні ноти
- - %d нейтральних нот
- - %d нейтральних нот
+ - %d нейтральний запис
+ - %d нейтральні записи
+ - %d нейтральних записів
+ - %d нейтральних записів
- - Нова нейтральна нота
- - Нова нейтральна нота
- - Нова нейтральна нота
- - Нові нейтральні ноти
+ - Новий нейтральний запис
+ - Нові нейтральні записи
+ - Нові нейтральні записи
+ - Нові нейтральні записи
- - Ви отримали %1$d нову нейтральну ноту
- - Ви отримали %1$d нові нейтральні ноти
- - Ви отримали %1$d нових нейтральних нот
- - Ви отримали %1$d нових нейтральних нот
+ - Ви отримали %1$d новий нейтральний запис
+ - Ви отримали %1$d нові нейтральні записи
+ - Ви отримали %1$d нових нейтральних записів
+ - Ви отримали %1$d нових нейтральних записів
- Брак домашніх завдань
- Позначити як зроблене
- Позначити як не зроблене
- Додати домашню роботу
- Домашню роботу додано успішно
- Домашню роботу видалено успішно
- Додатки
+ Немає домашніх завдань
+ Зроблено
+ Не зроблено
+ Додати домашнє завдання
+ Домашнє завдання додано успішно
+ Домашнє завдання видалено успішно
+ Вкладення
- - Нова домашня робота
- - Нова домашня робота
- - Нова домашня робота
- - Нова домашня робота
+ - Нове домашнє завдання
+ - Нові домашні завдання
+ - Нові домашні завдання
+ - Нові домашні завдання
- - Ви отримали %d нове домашнє завдання
- - Ви отримали %d нове домашнє завдання
- - Ви отримали %d нове домашнє завдання
- - Ви отримали %d нове домашнє завдання
+ - Ви отримали %d домашнє завдання
+ - Ви отримали %d домашні завдання
+ - Ви отримали %d домашніх завдань
+ - Ви отримали %d домашніх завдань
- %d домашнє завдання
- - %d домашнє завдання
- - %d домашнє завдання
- - %d домашнє завдання
+ - %d домашні завдання
+ - %d домашні завдання
+ - %d домашні завдання
Щасливий номер
Сьогоднішній щасливий номер
- Брак інформації о щасливому номері
+ Немає інформації о щасливому номері
Сьогоднішній щасливий номер
Сьогоднішній щасливий номер: %s
Показати історію
- Історія щасливих чисел
+ Історія щасливих номерів
Немає інформації про щасливі номери
Мобільні пристрої
- Брак пристроїв
+ Немає пристроїв
Видалити
Пристрій видалено
QR-код
@@ -441,98 +441,98 @@
Школа та вчителі
Школа
- Брак інформації про школу
+ Немає інформації про школу
Назва школи
Адреса школи
Телефон
Директор
- Викладач
+ Педагог
Показати на мапі
- Зателефонувати
+ Задзвонити
Вчителі
- Брак інформації про вчителів
- Брак предмету
+ Немає інформацій про вчителів
+ Немає предмету
Зустрічі
Немає інформації про зустрічі
- - %d конференція
- - %d конференція
- - %d конференція
- - %d конференцій
+ - %d зустріч
+ - %d зустрічі
+ - %d зустрічей
+ - %d зустрічей
- - Нова конференція
- - Нова конференція
- - Нова конференція
- - Нові конференції
+ - Нова зустріч
+ - Нові зустрічі
+ - Нові зустрічі
+ - Нові зустрічі
- - У вас є %1$d нова конференція
- - У вас є %1$d нова конференція
- - У вас є %1$d нова конференція
- - У вас є %1$d нових конференцій
+ - У вас є %1$d нова зустріч
+ - У вас є %1$d нові зустрічі
+ - У вас є %1$d нових зустрічей
+ - У вас є %1$d нових зустрічей
- Присутність на конференції
+ Присутність на зустрічі
Порядок денний
Оголошення школи
- Жодних навчальних оголошень
+ Немає шкільних оголошень
- - %d оголошення про школу
- - %d оголошення про школу
- - %d оголошення про школу
- - Оголошення нової школи
+ - %d шкільне оголошення
+ - %d шкільних оголошення
+ - %d шкільних оголошень
+ - %d шкільних оголошень
- - Оголошення нової школи
- - Оголошення нової школи
+ - Нове шкільне оголошення
+ - Нові шкільні оголошення
- Нові шкільні оголошення
- Нові шкільні оголошення
- - У вас є %1$d нове оголошення про школу
- - У вас є %1$d нове оголошення про школу
- - У вас є %1$d нове оголошення про школу
- - У вас є %1$d нових оголошень про школи
+ - У вас є %1$d нове шкільне оголошення
+ - У вас є %1$d нові шкільні оголошення
+ - У вас є %1$d нових шкільних оголошень
+ - У вас є %1$d нових шкільних оголошень
- Додати аккаунт
+ Додати обліковий запис
Вийти
- Ви впевнені, що хочете вийти з цього аккаунту?
- Вийти з аккаунту учня
- Студентський рахунок
- Головний рахунок
- Редагувати дані
- Менеджер аккаунтів
- Виберіть учня
+ Ви впевнені, що хочете вийти з цього облікового запису?
+ Вийти з облікового запису учня
+ Обліковий запис учня
+ Обліковий запис батька
+ Змінити дані
+ Змінити облікові записи
+ Вибрати учня
Сім\'я
Контакт
Деталі проживання
- Особиста інформація
+ Персональні дані
Версія додатка
Розробники
- Список розробників \"Wulkanowy\"
+ Список розробників додатку
Виникла помилка?
Повідомити о помилці за допомогою e-mail
FAQ
- Запитання, які часто задають
+ Найбільш поширенні питання
Сервер Discord
- Приєднатися до спільноти додатка
- Фен-сторінка Facebook
- Сторінка Twitter
+ Приєднуйтеся до спільноти додатка
+ Сторінка у Facebook
+ Сторінка у Twitter
Стежте за нами у Твіттері
- Вподобати нашу фансторінку у Facebook
+ Вподобайте нас у Facebook
Політика конфіденційності
Правила зберігання особистих даних
Налаштування системи
Відкрити налаштування системи
Домашня сторінка
- Допомогти розвитку додатка
+ Відвідайте наш сайт і допоможіть в розробці додатку
Ліцензії
- Ліцензії вжитих бібліотек
+ Ліцензії використаних бібліотек
Ліцензія
@@ -544,12 +544,12 @@
Друге ім\'я
Стать
Польське громадянство
- Прізвище
+ Дошлюбне прізвище
Імена батька і матері
- Номер телефону
+ Стаціонарний телефон
Мобільний телефон
- Електронна пошта
- Місця проживання
+ E-mail
+ Місце проживання
Адреса реєстрації
Адреса для кореспонденції
Прізвище та ім\'я
@@ -557,9 +557,9 @@
Адреса
Телефони
Чоловіча
- Жінка
+ Жіноча
Прізвище
- Охоронець
+ Опікун
Псевдонім
Додати псевдонім
@@ -570,73 +570,73 @@
Уроки
(Завтра)
- (сьогодні та завтра)
+ (Сьогодні та завтра)
Через мить:
Незабаром:
- Перше:
+ Перший:
Зараз:
Кінець уроків
Далі:
- Пізніше :
+ Пізніше:
- - %1$d більше уроку
- - %1$d більше уроку
- - %1$d більше уроку
- - %1$d більше уроків
+ - ще %1$d урок
+ - ще %1$d уроки
+ - ще %1$d уроків
+ - ще %1$d уроків
до %1$s
Немає майбутніх уроків
Сталася помилка під час завантаження уроків
- Домашня робота
+ Домашнє завдання
Немає домашнього завдання для виконання
- Помилка при завантаженні домашньої роботи
+ Помилка при завантаженні домашніх завдань
- - Ще %1$d домашнє завдання
- - Ще %1$d домашнє завдання
- - Ще %1$d домашнє завдання
- - Ще %1$d домашнє завдання
+ - ще %1$d домашнє завдання
+ - ще %1$d домашніх завдання
+ - ще %1$d домашніх завдань
+ - ще %1$d домашніх завдань
до %1$s
- Останні оцінки
+ Остатні оцінки
Немає нових оцінок
- Помилка при завантаженні класів
- Оголошення школи
+ Помилка при завантаженні оцінок
+ Шкільні оголошення
Немає поточних оголошень
Помилка при завантаженні анонсів
- - %1$d нових оголошень
- - %1$d нових оголошень
- - %1$d нових оголошень
- - %1$d нових оголошень
+ - ще %1$d оголошення
+ - ще %1$d оголошення
+ - ще %1$d оголошень
+ - ще %1$d оголошень
- Іспити
- Немає майбутніх іспитів
- Помилка при завантаженні іспитів
+ Тести
+ Немає майбутніх тестів
+ Помилка при завантаженні тестів
- - %1$d ще екзамен
- - %1$d ще екзамен
- - %1$d ще екзамен
- - %1$d ще іспитів
+ - ще %1$d тест
+ - ще %1$d тести
+ - ще %1$d тестів
+ - ще %1$d тестів
- Конференції
- Немає майбутніх конференцій
- Помилка при завантаженні конференцій
+ Зустрічі
+ Немає майбутніх зустрічей
+ Помилка при завантаженні зустрічей
- - Ще %1$d конференція
- - Ще %1$d конференція
- - Ще %1$d конференція
- - %1$d більше конференцій
+ - ще %1$d зустріч
+ - ще %1$d зустрічі
+ - ще %1$d зустрічей
+ - ще %1$d зустрічей
- Помилка при завантаженні даних
+ Виникла помилка при завантаженні даних
Нічого
- Провірити наявність оновлень
- Перед тим, як повідомлювати о помілці, перевірте наявність оновлень
+ Перевірити наявність оновлень
+ Перед тим, як повідомлювати о помилці, перевірте наявність оновлень
Зміст
Повторити
Опис
- Брак опису
+ Немає опису
Вчитель
Дата
Дата запису
@@ -653,26 +653,26 @@
Так
Ні
Зберегти
- Титул
+ Заголовок
Додати
Скопійовано
Відмінити
Змінити
Додати у календар
- Брак уроків
+ Немаэ уроків
Увібрати тему
Яскрава
Темна
Тема системи
- Додатки
+ Додаток
Вікно за замовчуванням
- Розрахункові середні параметри
- Примусово розрахувати середню оцінку через додаток
- Показати присутність
+ Параметри розраховування середніх оцінок
+ Примусово розраховувати середню оцінку через додаток
+ Показувати присутність
Тема
- Розширення оцінок
+ Розгортання оцінок
Позначити поточний урок
Показувати групи поруч з темами
Показувати діаграми в оцінках класу
@@ -685,102 +685,102 @@
Показувати повідомлення
Показувати повідомлення о наступних уроках
Зробити сповіщення майбутнього уроку нестійкими
- Вимкнути коли сповіщення не показуються у відстежувачі/темпі
+ Вимкніть, якщо сповіщення не показуються на розумному годиннику
Відкрити налаштування сповіщень системи
Виправити помилки з синхронізацією і повідомленнями
- На вашому пристрої можуть бути помилки з синхронізацією і повідомленнями\n\nЩоб виправити іх, вам необхідно додати Wulkanowy в авто-старт и вимкнути оптимізацію/экономію батареї в налаштуваннях пристрою.
+ На вашому пристрої можуть бути помилки з синхронізацією і повідомленнями\n\nЩоб виправити іх, вам необхідно додати Wulkanowy в авто-старт і вимкнути оптимізацію/экономію батареї в налаштуваннях пристрою.
Показувати дебаг-повідомлення
Синхронізація вимкнена
- Офіційні сповіщення додатків
- Захоплювати офіційні сповіщення програм
- Видалити офіційні сповіщення програм після захоплення
- Показувати push-повідомлення
- За допомогою цієї функції ви можете отримати заміну push -повідомлень, як у офіційному додатку. Все, що вам потрібно зробити, це дозволити Wulkanowy отримувати всі сповіщення у налаштуваннях вашої системи. \ N \ nЯк це працює? \ NКоли ви отримаєте сповіщення у Dziennik VULCAN, Wulkanowy отримає сповіщення (для цього призначені ці додаткові дозволи) і запустить синхронізація, яка може надсилати власне сповіщення. \ n \ n ТІЛЬКИ ДЛЯ РОЗШИРЕНИХ КОРИСТУВАЧІВ
+ Сповіщення офіційного додатку
+ Захоплювати сповіщення офіційного додатку
+ Видалити сповіщення офіційного додатку після захоплення
+ Захоплювати сповіщення
+ За допомогою цієї функції ви можете отримати аналог повідомлень з офіційного додатку. Все, що вам потрібно зробити, це дозволити Wulkanowy отримувати всі сповіщення у налаштуваннях вашої системи.\n\nЯк це працює?\nКоли ви отримаєте сповіщення у Dziennik VULCAN, Wulkanowy отримає сповіщення (для цього призначені ці додаткові дозволи) і запустить синхронізацію, щоб надіслати своє власне повідомлення.\n\nТІЛЬКИ ДЛЯ ПРОСУНУТИХ КОРИСТУВАЧІВ
Показувати повідомлення о наступних уроках
- Ви повинні дозволити Wulkanowy встановити будильник та нагадування у налаштуваннях вашої системи для використання цієї функції.
+ Ви повинні дозволити Wulkanowy встановлювати будильник та нагадування у налаштуваннях вашої системи для використання цієї функції.
Перейти до налаштувань
Синхронізація
- Автоматична синхронізація
+ Автоматичне оновлення
Призупинено на час канікул
Інтервал оновлення
Тільки через Wi-Fi
Синхронізувати
Синхронізовано!
Синхронізація не вдалася
- Триває синхронізація
+ Синхронізація...
Остання синхронізація: %s
Вартість плюсу
- Вага мінуса
+ Вартість мінуса
Відповісти з історією повідомлень
- Показувати в середньому арифметику, якщо немає ваги
+ Вилічити середню аритметичну, якщо оцінка немає вартості
Підтримка
- Відстежуйте єдину рекламу для підтримки проекту
+ Подивіться одну рекламу для підтримки проєкту
Згода в обробці даних
Щоб переглянути рекламу, ви повинні погодитися з умовами обробки даних нашої Політики конфіденційності
Погоджуюсь
Політика конфіденційності
Реклама завантажується
- Дякуємо за вашу підтримку, повертайтеся пізніше для більшої кількості оголошень
+ Дякуємо за вашу підтримку, повертайтеся пізніше для більшої кількості реклам
Додатково
- Вигляд & Поведінка
- Повідомлення
+ Вигляд та поведінка
+ Сповіщення
Синхронізація
Реклама
Оцінки
- Дошка
+ Головна
Видимість плиток
Відвідуваність
Розклад
- Класи
- Обчислена середня
+ Оцінки
+ Розрахована середня оцінка
Повідомлення
- Вигляд & Поведінка
- Мови, теми, тема сортування
- Сповіщення додатку, виправляти проблеми
- Повідомлення
+ Вигляд та поведінка
+ Мова, тема, сортування предметів
+ Налаштуванная сповіщень додатку
+ Сповіщення
Синхронізація
- Автоматичне оновлення, інтервал синхронізації
- Плюс і мінус значення, середні обчислення
+ Автоматичне оновлення та його інтервал
+ Вартість плюса та мінуса, розраховування середньої
Додатково
- Версія програми, учасники, соціальні портали
- Відображається реклама, підтримка проектів
+ Версія додатку, учасники, соціальні портали
+ Показування реклами, підтримка проєкту
Нові оцінки
- Нова домашня робота
- Нові конференції
- Нові іспити
+ Нові домашні завдання
+ Нові зустрічі
+ Нові тести
Щасливий номер
Нові повідомлення
- Нові нотатки
- Оголошення нової школи
- Показувати push-повідомлення
+ Нові зауваження
+ Нові шкільні оголошення
+ Показувати сповіщення від розробників
Наступні уроки
- Дебаг
- Зміна у розкладі
- Нова відвідуваність
+ Відладка
+ Зміни у розкладі
+ Нові записи відвідуваності
Чорний
Червоний
Голубий
Зелений
Фіолетовий
- Брак кольору
+ Без кольору
Завантаження оновлень розпочато…
Щойно завантажено оновлення.
- Перезапустити
+ Перезавантажити
Помилка оновлення! Wulkanowy може не працювати належним чином. Подумайте про оновлення
- Брак з\'єднання з інтернетом
+ Немає з\'єднання з інтернетом
Сталася помилка. Перевірте годинник пристрою
- Помилка підключення до реєстрації. Сервери можуть бути перевантажені. Будь-ласка спробуйте пізніше
- Помилка завантаження даних. Будь-ласка спробуйте пізніше
- Потрібна реєстрація зміни пароля
- Технічна перерва в журналі UONET + продовжується. Спробуйте пізніше
- Невідома помилка реєстру UONET+. Спробуйте ще раз пізніше
- Невідома помилка програми. Будь-ласка спробуйте пізніше
+ Помилка підключення до щоденнику. Сервери можуть бути перевантажені, спробуйте пізніше
+ Помилка завантаження даних, спробуйте пізніше
+ Необхідна зміна пароля щоденника
+ UONET+ проводить технічне осблуговування, спробуйте пізніше
+ Невідома помилка щоденника UONET+, спробуйте пізніше
+ Невідома помилка програми, спробуйте пізніше
Відбулася несподівана помилка
- Функція вимкнена школою
- Функція не доступна в цьому режимі
- Це поле обов\'язкове для заповнення
+ Функція вимкнена вашою школою
+ Функція недоступна в режимі Mobile API. Увійдіть в інший режим
+ Це поле обовʼязкове
From 637125e1fc432bca4875175fd1ed41877f966d44 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 14 May 2022 13:51:39 +0000
Subject: [PATCH 113/669] Bump hilt_version from 2.41 to 2.42 (#1856)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 862a45ccb..1d9b797b7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ buildscript {
ext {
kotlin_version = '1.6.21'
about_libraries = '8.9.4'
- hilt_version = "2.41"
+ hilt_version = "2.42"
}
repositories {
mavenCentral()
From c296e72c3042395e2149d54e00b95c4980869ba6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 14 May 2022 13:52:16 +0000
Subject: [PATCH 114/669] Bump mockk from 1.12.2 to 1.12.4 (#1854)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 2910c84b7..142c7cbd7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -173,7 +173,7 @@ ext {
android_hilt = "1.0.0"
room = "2.4.2"
chucker = "3.5.2"
- mockk = "1.12.2"
+ mockk = "1.12.4"
coroutines = "1.6.1"
}
From b17e9deca01ceb297c07ed90da36012d51c06e00 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 14 May 2022 13:52:41 +0000
Subject: [PATCH 115/669] Bump kotlinx-serialization-json from 1.3.2 to 1.3.3
(#1853)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 142c7cbd7..7e9bdedf3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -182,7 +182,7 @@ dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
- implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
implementation "androidx.core:core-ktx:1.7.0"
From 0c8e2632a28dbca59a44bcc8b3f80bc704a16ec6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 14 May 2022 13:52:58 +0000
Subject: [PATCH 116/669] Bump firebase-bom from 30.0.0 to 30.0.1 (#1851)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 7e9bdedf3..3cffcce8c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -233,7 +233,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
- playImplementation platform('com.google.firebase:firebase-bom:30.0.0')
+ playImplementation platform('com.google.firebase:firebase-bom:30.0.1')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 459c8330f905b3718f4a8ea624be9868d042f084 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 14 May 2022 13:53:13 +0000
Subject: [PATCH 117/669] Bump hianalytics from 6.4.1.302 to 6.5.0.300 (#1852)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 3cffcce8c..dc289442b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
playImplementation 'com.google.android.play:core-ktx:1.8.1'
playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
- hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.302'
+ hmsImplementation 'com.huawei.hms:hianalytics:6.5.0.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.6.200'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From facf84d9a811f614dc454bffba5e51a8540a1409 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 14 May 2022 14:35:58 +0000
Subject: [PATCH 118/669] Bump about_libraries from 8.9.4 to 10.2.0 (#1858)
---
app/build.gradle | 2 +-
.../hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt | 3 ++-
.../wulkanowy/ui/modules/about/license/LicenseAdapter.kt | 5 +++--
.../wulkanowy/ui/modules/about/license/LicenseFragment.kt | 5 ++++-
.../wulkanowy/ui/modules/about/license/LicensePresenter.kt | 2 +-
build.gradle | 2 +-
6 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index dc289442b..c033c7d52 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -132,7 +132,7 @@ android {
kotlinOptions {
jvmTarget = "11"
- freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]
+ freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]
}
packagingOptions {
diff --git a/app/src/hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt
index fb9bcae6c..adb162fd7 100644
--- a/app/src/hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt
+++ b/app/src/hms/java/io/github/wulkanowy/utils/InAppReviewHelper.kt
@@ -7,6 +7,7 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
+@Suppress("UNUSED_PARAMETER", "unused")
class InAppReviewHelper @Inject constructor(
@ApplicationContext private val context: Context
) {
@@ -14,4 +15,4 @@ class InAppReviewHelper @Inject constructor(
fun showInAppReview(activity: MainActivity) {
// do nothing
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt
index 6ae06bbe7..adf4ca741 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicenseAdapter.kt
@@ -23,8 +23,9 @@ class LicenseAdapter @Inject constructor() : RecyclerView.Adapter(R.layout.fragment_l
override val titleStringId get() = R.string.license_title
- override val appLibraries by lazy { Libs(requireContext()).libraries }
+ override val appLibraries by lazy {
+ Libs.Builder().withContext(requireContext()).build().libraries
+ }
companion object {
fun newInstance() = LicenseFragment()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt
index 5aa12a575..ddcd5918f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/license/LicensePresenter.kt
@@ -22,7 +22,7 @@ class LicensePresenter @Inject constructor(
}
fun onItemSelected(library: Library) {
- view?.run { library.licenses?.firstOrNull()?.licenseDescription?.let { openLicense(it) } }
+ view?.run { library.licenses.firstOrNull()?.licenseContent?.let { openLicense(it) } }
}
private fun loadData() {
diff --git a/build.gradle b/build.gradle
index 1d9b797b7..c87ec1506 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
buildscript {
ext {
kotlin_version = '1.6.21'
- about_libraries = '8.9.4'
+ about_libraries = '10.2.0'
hilt_version = "2.42"
}
repositories {
From c2496a15b8864bdf3b8e5f88c1c79973bdd13d4e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 14 May 2022 14:36:09 +0000
Subject: [PATCH 119/669] Bump flow-preferences from 1.6.0 to 1.7.0 (#1859)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index c033c7d52..0210e5a79 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -231,7 +231,7 @@ dependencies {
implementation "io.coil-kt:coil:1.4.0"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
- implementation 'com.fredporciuncula:flow-preferences:1.6.0'
+ implementation 'com.fredporciuncula:flow-preferences:1.7.0'
playImplementation platform('com.google.firebase:firebase-bom:30.0.1')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
From dbba61a99f57522d56ba917c1bd956efec1a03eb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 14 May 2022 14:48:58 +0000
Subject: [PATCH 120/669] Bump coil from 1.4.0 to 2.0.0 (#1855)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 0210e5a79..ac03aca53 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -228,7 +228,7 @@ dependencies {
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
- implementation "io.coil-kt:coil:1.4.0"
+ implementation "io.coil-kt:coil:2.0.0"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.7.0'
From 9542b9f231a7b855b86ae8d1f9199f9ee24a90ba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Mon, 16 May 2022 23:23:49 +0200
Subject: [PATCH 121/669] Set "no data" string if attendance subject is blank
(#1864)
---
app/build.gradle | 2 +-
.../wulkanowy/ui/modules/attendance/AttendanceAdapter.kt | 8 +++++---
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index ac03aca53..be2499e47 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -178,7 +178,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:1.6.0"
+ implementation "io.github.wulkanowy:sdk:189f5ecee0"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
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
index 5d5ed504c..39f376f65 100644
--- 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
@@ -35,9 +35,11 @@ class AttendanceAdapter @Inject constructor() :
with(holder.binding) {
attendanceItemNumber.text = item.number.toString()
- attendanceItemSubject.text = item.subject
+ attendanceItemSubject.text = item.subject.ifBlank {
+ root.context.getString(R.string.all_no_data)
+ }
attendanceItemDescription.setText(item.descriptionRes)
- attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE }
+ attendanceItemAlert.isVisible = item.let { it.absence && !it.excused }
attendanceItemNumber.visibility = View.GONE
attendanceItemExcuseInfo.visibility = View.GONE
attendanceItemExcuseCheckbox.visibility = View.GONE
@@ -46,7 +48,7 @@ class AttendanceAdapter @Inject constructor() :
onExcuseCheckboxSelect(item, checked)
}
- when (item.excuseStatus?.let { SentExcuseStatus.valueOf(it)}) {
+ when (item.excuseStatus?.let { SentExcuseStatus.valueOf(it) }) {
SentExcuseStatus.WAITING -> {
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_waiting)
attendanceItemExcuseInfo.visibility = View.VISIBLE
From 653417668558ff3221e26a40e51dc8320f58db0e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Mon, 16 May 2022 23:42:40 +0200
Subject: [PATCH 122/669] Version 1.6.4
---
app/build.gradle | 8 ++++----
app/src/main/play/release-notes/pl-PL/default.txt | 8 ++------
2 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index be2499e47..7db0acebb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -22,8 +22,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 31
- versionCode 107
- versionName "1.6.3"
+ versionCode 108
+ versionName "1.6.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -154,7 +154,7 @@ play {
track = 'production'
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
userFraction = 0.50d
- updatePriority = 1
+ updatePriority = 3
enabled.set(false)
}
@@ -178,7 +178,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:189f5ecee0"
+ implementation "io.github.wulkanowy:sdk:1.6.4"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
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 2dba56ddd..b6340bb93 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,5 @@
-Wersja 1.6.3
+Wersja 1.6.4
-- dodaliśmy możliwość usuwania wielu wiadomości jednocześnie
-- dodaliśmy opcję szybkiego dodawania sprawdzianów do kalendarza
-- dodaliśmy średnią ucznia w wykresach ocen klasy
-- naprawiliśmy rzadki błąd dotyczący problemów z automatycznym odświeżaniem ekranu startowego
-- naprawiliśmy błąd z liczeniem średniej w drugim semestrze
+- naprawiliśmy błąd ładowania frekwencji na GPE i Lubelskim Portalu Oświatowym
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
From a2804d813a73014e8b8d27bdc381b7124f1322d7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 24 May 2022 08:41:23 +0000
Subject: [PATCH 123/669] Bump firebase-bom from 30.0.1 to 30.0.2 (#1872)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 7db0acebb..57ad5c903 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -233,7 +233,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.7.0'
- playImplementation platform('com.google.firebase:firebase-bom:30.0.1')
+ playImplementation platform('com.google.firebase:firebase-bom:30.0.2')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From dc717c9fb55a8c7d7305be76e19f68e2553ac31f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 24 May 2022 08:43:32 +0000
Subject: [PATCH 124/669] Bump core-splashscreen from 1.0.0-beta02 to
1.0.0-rc01 (#1871)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 57ad5c903..2cfb7bc6d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -186,7 +186,7 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
implementation "androidx.core:core-ktx:1.7.0"
- implementation 'androidx.core:core-splashscreen:1.0.0-beta02'
+ implementation 'androidx.core:core-splashscreen:1.0.0-rc01'
implementation "androidx.activity:activity-ktx:1.4.0"
implementation "androidx.appcompat:appcompat:1.4.1"
implementation "androidx.fragment:fragment-ktx:1.4.1"
From 808927a58a2e90ee7ddea790d645e407c26d71dd Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 24 May 2022 08:43:52 +0000
Subject: [PATCH 125/669] Bump coil from 2.0.0 to 2.1.0 (#1870)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 2cfb7bc6d..b05e0501c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -228,7 +228,7 @@ dependencies {
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
- implementation "io.coil-kt:coil:2.0.0"
+ implementation "io.coil-kt:coil:2.1.0"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.7.0'
From fa48b033af2fa28f53acdea7d6c35eda4615fb35 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 24 May 2022 08:44:17 +0000
Subject: [PATCH 126/669] Bump constraintlayout from 2.1.3 to 2.1.4 (#1869)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index b05e0501c..ab1593f82 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -196,7 +196,7 @@ dependencies {
implementation "androidx.recyclerview:recyclerview:1.2.1"
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
- implementation "androidx.constraintlayout:constraintlayout:2.1.3"
+ implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
implementation "com.google.android.material:material:1.5.0"
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
From c42a47ac48007e2db05f8f7a1c563414fcad0e7c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 26 May 2022 04:18:37 +0000
Subject: [PATCH 127/669] Bump material from 1.5.0 to 1.6.0 (#1846)
---
app/build.gradle | 2 +-
.../ui/modules/timetablewidget/TimetableWidgetProvider.kt | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index ab1593f82..57ba0f591 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -198,7 +198,7 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
- implementation "com.google.android.material:material:1.5.0"
+ implementation "com.google.android.material:material:1.6.0"
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation 'com.github.lopspower:CircularImageView:4.2.0'
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 07e717eaf..745769864 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
@@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.timetablewidget
-import android.annotation.SuppressLint
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetManager.*
@@ -132,7 +131,6 @@ class TimetableWidgetProvider : BroadcastReceiver() {
}
}
- @SuppressLint("DefaultLocale")
private fun updateWidget(
context: Context,
appWidgetId: Int,
From fcf0adfd807015178cdd9bb631c4784cf8f0380f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 26 May 2022 05:26:52 +0000
Subject: [PATCH 128/669] Bump gradle from 7.1.3 to 7.2.0 (#1857)
---
app/build.gradle | 7 +++++--
app/src/main/AndroidManifest.xml | 1 -
build.gradle | 2 +-
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 57ba0f591..2a36b19a9 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -15,6 +15,7 @@ apply from: 'sonarqube.gradle'
apply from: 'hooks.gradle'
android {
+ namespace 'io.github.wulkanowy'
compileSdkVersion 31
defaultConfig {
@@ -136,8 +137,10 @@ android {
}
packagingOptions {
- exclude 'META-INF/library_release.kotlin_module'
- exclude 'META-INF/library-core_release.kotlin_module'
+ resources {
+ excludes += ['META-INF/library_release.kotlin_module',
+ 'META-INF/library-core_release.kotlin_module']
+ }
}
aboutLibraries {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 72fee08a7..7835db902 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,7 +1,6 @@
diff --git a/build.gradle b/build.gradle
index c87ec1506..d778e9a63 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
- classpath 'com.android.tools.build:gradle:7.1.3'
+ classpath 'com.android.tools.build:gradle:7.2.1'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.huawei.agconnect:agcp:1.6.6.200'
From 5c4a3d578bad1b5ace29a395b94a150529ca9c8b Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Fri, 27 May 2022 22:19:22 +0200
Subject: [PATCH 129/669] Add grade sorting by average (#1863)
---
.../wulkanowy/data/enums/GradeSortingMode.kt | 5 +++--
.../grade/details/GradeDetailsPresenter.kt | 5 +++--
.../grade/summary/GradeSummaryPresenter.kt | 16 +++++++++++++++-
app/src/main/res/values/preferences_values.xml | 2 ++
4 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt b/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt
index c5c0196c9..a7aa4cc2f 100644
--- a/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/enums/GradeSortingMode.kt
@@ -2,9 +2,10 @@ package io.github.wulkanowy.data.enums
enum class GradeSortingMode(val value: String) {
ALPHABETIC("alphabetic"),
- DATE("date");
+ DATE("date"),
+ AVERAGE("average");
companion object {
fun getByValue(value: String) = values().find { it.value == value } ?: ALPHABETIC
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
index 746601a68..8cde5d6b9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
@@ -3,8 +3,8 @@ package io.github.wulkanowy.ui.modules.grade.details
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.enums.GradeExpandMode
-import io.github.wulkanowy.data.enums.GradeSortingMode.ALPHABETIC
-import io.github.wulkanowy.data.enums.GradeSortingMode.DATE
+import io.github.wulkanowy.data.enums.GradeSortingMode
+import io.github.wulkanowy.data.enums.GradeSortingMode.*
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
@@ -204,6 +204,7 @@ class GradeDetailsPresenter @Inject constructor(
ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage ->
gradeDetailsWithAverage.subject.lowercase()
}
+ AVERAGE -> gradeSubjects.sortedByDescending { it.average }
}
}
.map { (subject, average, points, _, grades) ->
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt
index b07570cb2..4d5a43d8f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt
@@ -2,6 +2,9 @@ package io.github.wulkanowy.ui.modules.grade.summary
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.GradeSummary
+import io.github.wulkanowy.data.enums.GradeSortingMode
+import io.github.wulkanowy.data.enums.GradeSortingMode.*
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
@@ -14,6 +17,7 @@ import javax.inject.Inject
class GradeSummaryPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
+ private val preferencesRepository: PreferencesRepository,
private val averageProvider: GradeAverageProvider,
private val analytics: AnalyticsHelper
) : BasePresenter(errorHandler, studentRepository) {
@@ -127,7 +131,17 @@ class GradeSummaryPresenter @Inject constructor(
private fun createGradeSummaryItems(items: List): List {
return items
.filter { !checkEmpty(it) }
- .sortedBy { it.subject }
+ .let { gradeSubjects ->
+ when (preferencesRepository.gradeSortingMode) {
+ DATE -> gradeSubjects.sortedByDescending { gradeDetailsWithAverage ->
+ gradeDetailsWithAverage.grades.maxByOrNull { it.date }?.date
+ }
+ ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage ->
+ gradeDetailsWithAverage.subject.lowercase()
+ }
+ AVERAGE -> gradeSubjects.sortedByDescending { it.average }
+ }
+ }
.map { it.summary.copy(average = it.average) }
}
diff --git a/app/src/main/res/values/preferences_values.xml b/app/src/main/res/values/preferences_values.xml
index 1d777bdb6..312f0b878 100644
--- a/app/src/main/res/values/preferences_values.xml
+++ b/app/src/main/res/values/preferences_values.xml
@@ -82,10 +82,12 @@
- Alphabetically
- By date
+ - By average
- alphabetic
- date
+ - average
From 891e241d1a7cb516a9233d0e5c270d0593e35600 Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Sat, 28 May 2022 02:03:42 +0200
Subject: [PATCH 130/669] Hide account selector in MessagePreviewView and
SendMessageView (#1866)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Rafał Borcz
---
.../message/preview/MessagePreviewFragment.kt | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
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 860ecc571..4b2685c6d 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
@@ -57,7 +57,8 @@ class MessagePreviewFragment :
get() = getString(R.string.message_no_subject)
override val printHTML: String
- get() = requireContext().assets.open("message-print-page.html").bufferedReader().use { it.readText() }
+ get() = requireContext().assets.open("message-print-page.html").bufferedReader()
+ .use { it.readText() }
override val messageNotExists: String
get() = getString(R.string.message_not_exists)
@@ -81,7 +82,10 @@ class MessagePreviewFragment :
super.onViewCreated(view, savedInstanceState)
binding = FragmentMessagePreviewBinding.bind(view)
messageContainer = binding.messagePreviewContainer
- presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as? Message)
+ presenter.onAttachView(
+ this,
+ (savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as? Message
+ )
}
override fun initView() {
@@ -101,6 +105,8 @@ class MessagePreviewFragment :
menuShareButton = menu.findItem(R.id.messagePreviewMenuShare)
menuPrintButton = menu.findItem(R.id.messagePreviewMenuPrint)
presenter.onCreateOptionsMenu()
+
+ menu.findItem(R.id.mainMenuAccount).isVisible = false
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@@ -173,7 +179,8 @@ class MessagePreviewFragment :
val webView = WebView(requireContext())
webView.webViewClient = object : WebViewClient() {
- override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false
+ override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) =
+ false
override fun onPageFinished(view: WebView, url: String) {
createWebPrintJob(view, jobName)
From d2d1d1dba71a7e6b03a78fe921fc15534cffd080 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 31 May 2022 13:50:03 +0000
Subject: [PATCH 131/669] Bump firebase-bom from 30.0.2 to 30.1.0 (#1878)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 2a36b19a9..691d00f64 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -236,7 +236,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.7.0'
- playImplementation platform('com.google.firebase:firebase-bom:30.0.2')
+ playImplementation platform('com.google.firebase:firebase-bom:30.1.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From d074e5c9b322e317e010ffb0744b643f2df2541a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 31 May 2022 13:50:22 +0000
Subject: [PATCH 132/669] Bump play-services-ads from 20.6.0 to 21.0.0 (#1877)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 691d00f64..c9c7a9ef9 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -242,7 +242,7 @@ dependencies {
playImplementation 'com.google.firebase:firebase-crashlytics:'
playImplementation 'com.google.android.play:core:1.10.3'
playImplementation 'com.google.android.play:core-ktx:1.8.1'
- playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
+ playImplementation 'com.google.android.gms:play-services-ads:21.0.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.5.0.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.6.200'
From 4f3f24ac104a986a8dd42425668ed9ee576b825e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 31 May 2022 13:50:52 +0000
Subject: [PATCH 133/669] Bump about_libraries from 10.2.0 to 10.3.0 (#1876)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index d778e9a63..5a498be90 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
buildscript {
ext {
kotlin_version = '1.6.21'
- about_libraries = '10.2.0'
+ about_libraries = '10.3.0'
hilt_version = "2.42"
}
repositories {
From 8dcb3ed45d2625b4208df856099ad58c29a29d3b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 31 May 2022 13:51:12 +0000
Subject: [PATCH 134/669] Bump firebase-crashlytics-gradle from 2.8.1 to 2.9.0
(#1874)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 5a498be90..7640fbc48 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,7 +17,7 @@ buildscript {
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.huawei.agconnect:agcp:1.6.6.200'
- classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.0'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3"
From 8c515bd03f73f41d82f69c29a2d52f5537ae53c7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 31 May 2022 13:52:09 +0000
Subject: [PATCH 135/669] Bump coroutines from 1.6.1 to 1.6.2 (#1875)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index c9c7a9ef9..612cb9536 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -177,7 +177,7 @@ ext {
room = "2.4.2"
chucker = "3.5.2"
mockk = "1.12.4"
- coroutines = "1.6.1"
+ coroutines = "1.6.2"
}
dependencies {
From cce736410bd54e0f186191623be47c09582b89b3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 13 Jun 2022 04:59:31 +0000
Subject: [PATCH 136/669] Bump material from 1.6.0 to 1.6.1 (#1884)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 612cb9536..1038184f2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -201,7 +201,7 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
- implementation "com.google.android.material:material:1.6.0"
+ implementation "com.google.android.material:material:1.6.1"
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation 'com.github.lopspower:CircularImageView:4.2.0'
From 03ad5527f8d83526d751042a5ef32d77a6056392 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 13 Jun 2022 04:59:50 +0000
Subject: [PATCH 137/669] Bump appcompat from 1.4.1 to 1.4.2 (#1883)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 1038184f2..1f680e265 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -191,7 +191,7 @@ dependencies {
implementation "androidx.core:core-ktx:1.7.0"
implementation 'androidx.core:core-splashscreen:1.0.0-rc01'
implementation "androidx.activity:activity-ktx:1.4.0"
- implementation "androidx.appcompat:appcompat:1.4.1"
+ implementation "androidx.appcompat:appcompat:1.4.2"
implementation "androidx.fragment:fragment-ktx:1.4.1"
implementation "androidx.annotation:annotation:1.3.0"
From f61d820d6fd33babb7d85cee7dff22ba80eb5d42 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 13 Jun 2022 05:07:54 +0000
Subject: [PATCH 138/669] Bump core-ktx from 1.7.0 to 1.8.0 (#1882)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 1f680e265..aa7f6b37b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -188,7 +188,7 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
- implementation "androidx.core:core-ktx:1.7.0"
+ implementation "androidx.core:core-ktx:1.8.0"
implementation 'androidx.core:core-splashscreen:1.0.0-rc01'
implementation "androidx.activity:activity-ktx:1.4.0"
implementation "androidx.appcompat:appcompat:1.4.2"
From c3cbaa6ac2f4a58b36572ed64e0d2b68049c795f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Mon, 13 Jun 2022 07:43:12 +0200
Subject: [PATCH 139/669] Update dependencies (#1887)
---
app/build.gradle | 4 ++--
.../wulkanowy/ui/modules/login/LoginActivity.kt | 1 +
.../ui/modules/login/recover/LoginRecoverFragment.kt | 11 ++++-------
.../github/wulkanowy/ui/modules/main/MainActivity.kt | 1 +
build.gradle | 2 +-
5 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index aa7f6b37b..230afdfe9 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -16,13 +16,13 @@ apply from: 'hooks.gradle'
android {
namespace 'io.github.wulkanowy'
- compileSdkVersion 31
+ compileSdkVersion 32
defaultConfig {
applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
- targetSdkVersion 31
+ targetSdkVersion 32
versionCode 108
versionName "1.6.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
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 d7d77f73d..aac60b56d 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
@@ -93,6 +93,7 @@ class LoginActivity : BaseActivity(), Logi
}
//https://developer.android.com/guide/playcore/in-app-updates#status_callback
+ @Deprecated("Deprecated in Java")
@Suppress("DEPRECATION")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt
index c1c111d46..786bbfce8 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/recover/LoginRecoverFragment.kt
@@ -6,9 +6,7 @@ import android.os.Bundle
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
-import android.webkit.JavascriptInterface
-import android.webkit.WebView
-import android.webkit.WebViewClient
+import android.webkit.*
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import com.yariksoffice.lingver.Lingver
@@ -206,10 +204,9 @@ class LoginRecoverFragment :
}
override fun onReceivedError(
- view: WebView,
- errorCode: Int,
- description: String,
- failingUrl: String
+ view: WebView?,
+ request: WebResourceRequest?,
+ error: WebResourceError?
) {
recoverWebViewSuccess = false
}
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 1bfc8ba58..260cf76c5 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
@@ -100,6 +100,7 @@ class MainActivity : BaseActivity(), MainVie
}
//https://developer.android.com/guide/playcore/in-app-updates#status_callback
+ @Deprecated("Deprecated in Java")
@Suppress("DEPRECATION")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
diff --git a/build.gradle b/build.gradle
index 7640fbc48..3a8ae20cc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
buildscript {
ext {
- kotlin_version = '1.6.21'
+ kotlin_version = '1.7.0'
about_libraries = '10.3.0'
hilt_version = "2.42"
}
From 6b705835735892114de3d666817ee00a3b69a0f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Mon, 13 Jun 2022 07:43:40 +0200
Subject: [PATCH 140/669] New Crowdin updates (#1873)
---
app/src/main/res/values-cs/preferences_values.xml | 1 +
app/src/main/res/values-de/preferences_values.xml | 1 +
app/src/main/res/values-pl/preferences_values.xml | 1 +
app/src/main/res/values-pl/strings.xml | 2 +-
app/src/main/res/values-ru/preferences_values.xml | 1 +
app/src/main/res/values-sk/preferences_values.xml | 1 +
app/src/main/res/values-uk/preferences_values.xml | 1 +
7 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/app/src/main/res/values-cs/preferences_values.xml b/app/src/main/res/values-cs/preferences_values.xml
index 5252f79b8..c8731372f 100644
--- a/app/src/main/res/values-cs/preferences_values.xml
+++ b/app/src/main/res/values-cs/preferences_values.xml
@@ -34,6 +34,7 @@
- Abecedně
- Podle data
+ - By average
- Dzienniczek+
diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml
index 08b9d240b..097e90e96 100644
--- a/app/src/main/res/values-de/preferences_values.xml
+++ b/app/src/main/res/values-de/preferences_values.xml
@@ -34,6 +34,7 @@
- Alphabetisch
- Nach Datum
+ - By average
- Dzienniczek+
diff --git a/app/src/main/res/values-pl/preferences_values.xml b/app/src/main/res/values-pl/preferences_values.xml
index c823e9608..456005747 100644
--- a/app/src/main/res/values-pl/preferences_values.xml
+++ b/app/src/main/res/values-pl/preferences_values.xml
@@ -34,6 +34,7 @@
- Alfabetycznie
- Według daty
+ - Według średniej
- Dzienniczek+
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 1607b17c6..91e9fe7f3 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -94,7 +94,7 @@
Przewidywana ocena
Obliczona średnia
Jak działa obliczona średnia?
- Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\nŚrednia ocen tylko z wybranego semestru:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia ze średnich z obu semestrów:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semetrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia wszystkich ocen z całego roku:\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej z zsumowanych średnich
+ Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\nŚrednia ocen tylko z wybranego semestru:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia ze średnich z obu semestrów:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semestrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia wszystkich ocen z całego roku:\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej z zsumowanych średnich
Jak działa końcowa średnia?
Średnią końcową jest średnia arytmetyczna obliczona na podstawie wszystkich obecnie dostępnych ocen końcowych w danym semestrze.\n\nSchemat obliczeń składa się z następujących kroków:\n1. Sumowanie końcowych ocen wpisanych przez nauczycieli\n2. Dzielenie przez liczbę przedmiotów, z których oceny zostały już wystawione
Końcowa średnia
diff --git a/app/src/main/res/values-ru/preferences_values.xml b/app/src/main/res/values-ru/preferences_values.xml
index 9cc37620e..8a8c260da 100644
--- a/app/src/main/res/values-ru/preferences_values.xml
+++ b/app/src/main/res/values-ru/preferences_values.xml
@@ -34,6 +34,7 @@
- В алфавитном порядке
- По дате
+ - По средней
- Dzienniczek+
diff --git a/app/src/main/res/values-sk/preferences_values.xml b/app/src/main/res/values-sk/preferences_values.xml
index e64f5606a..ab0a43b64 100644
--- a/app/src/main/res/values-sk/preferences_values.xml
+++ b/app/src/main/res/values-sk/preferences_values.xml
@@ -34,6 +34,7 @@
- Abecedne
- Podľa dátumu
+ - By average
- Dzienniczek+
diff --git a/app/src/main/res/values-uk/preferences_values.xml b/app/src/main/res/values-uk/preferences_values.xml
index 82c5b6ecc..44acd18e9 100644
--- a/app/src/main/res/values-uk/preferences_values.xml
+++ b/app/src/main/res/values-uk/preferences_values.xml
@@ -34,6 +34,7 @@
- За алфавітом
- За датою
+ - За середньою
- Dzienniczek+
From 06ed5f6079ae17c2a63b2a9088fff476c5a4b4b2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 16 Jun 2022 21:53:21 +0000
Subject: [PATCH 141/669] Bump logging-interceptor from 4.9.3 to 4.10.0 (#1889)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 230afdfe9..f3f39258c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -225,7 +225,7 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
- implementation "com.squareup.okhttp3:logging-interceptor:4.9.3"
+ implementation "com.squareup.okhttp3:logging-interceptor:4.10.0"
implementation "com.jakewharton.timber:timber:5.0.1"
implementation "at.favre.lib:slf4j-timber:1.0.1"
From cd59166efbd4bea7f79f2cdfc262212e0fcee8d0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 16 Jun 2022 22:15:18 +0000
Subject: [PATCH 142/669] Bump sonarqube-gradle-plugin from 3.3 to 3.4.0.2513
(#1888)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 3a8ae20cc..9dde0b283 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,7 +20,7 @@ buildscript {
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.0'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3"
- classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3"
+ classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513"
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries"
}
From bfab265ccf4f5a0809788d92f535c7106bef8a0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sat, 18 Jun 2022 11:54:08 +0200
Subject: [PATCH 143/669] Fix case sensitive domain checker (#1894)
---
app/build.gradle | 2 +-
.../wulkanowy/ui/modules/login/form/LoginFormPresenter.kt | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index f3f39258c..e629134e0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -181,7 +181,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:1.6.4"
+ implementation "io.github.wulkanowy:sdk:16811fbe90"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
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 b4291ff47..0acb0ea6d 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
@@ -172,7 +172,7 @@ class LoginFormPresenter @Inject constructor(
if ("@" in login && "||" !in login && "login" !in host && "email" !in host) {
val emailHost = login.substringAfter("@")
val emailDomain = URL(host).host
- if (emailHost != emailDomain) {
+ if (!emailHost.equals(emailDomain, true)) {
view?.setErrorEmailInvalid(domain = emailDomain)
isCorrect = false
}
From 0a2eb0784469afd19c837af49a1e7d5e45001567 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sat, 18 Jun 2022 12:11:46 +0200
Subject: [PATCH 144/669] Fix date in attendance and timetable when day is
changing (#1893)
---
.../modules/attendance/AttendancePresenter.kt | 18 +++++++++++-------
.../ui/modules/timetable/TimetablePresenter.kt | 18 +++++++++++-------
2 files changed, 22 insertions(+), 14 deletions(-)
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 7fcbd002e..26bfaf19f 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
@@ -91,15 +91,19 @@ class AttendancePresenter @Inject constructor(
fun onViewReselected() {
Timber.i("Attendance view is reselected")
- view?.also { view ->
+ view?.let { view ->
if (view.currentStackSize == 1) {
- baseDate.also {
- if (currentDate != it) {
- reloadView(it)
- loadData()
- } else if (!view.isViewEmpty) view.resetView()
+ baseDate = now().previousOrSameSchoolDay
+
+ if (currentDate != baseDate) {
+ reloadView(baseDate)
+ loadData()
+ } else if (!view.isViewEmpty) {
+ view.resetView()
}
- } else view.popView()
+ } else {
+ view.popView()
+ }
}
}
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 dc6c89213..d06874082 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
@@ -87,15 +87,19 @@ class TimetablePresenter @Inject constructor(
fun onViewReselected() {
Timber.i("Timetable view is reselected")
- view?.also { view ->
+ view?.let { view ->
if (view.currentStackSize == 1) {
- baseDate.also {
- if (currentDate != it) {
- reloadView(it)
- loadData()
- } else if (!view.isViewEmpty) view.resetView()
+ baseDate = now().nextOrSameSchoolDay
+
+ if (currentDate != baseDate) {
+ reloadView(baseDate)
+ loadData()
+ } else if (!view.isViewEmpty) {
+ view.resetView()
}
- } else view.popView()
+ } else {
+ view.popView()
+ }
}
}
From a264abf8144f8ce4ba8f9370333ef8163a0e87d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sat, 18 Jun 2022 12:12:04 +0200
Subject: [PATCH 145/669] Fix typo in average description (#1892)
---
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 ff25da7f5..2ca516adf 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -104,7 +104,7 @@
Predicted grade
Calculated average
How does Calculated Average work?
- The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n3. Adding calculated averages\n4. Calculating the arithmetic average of summed averages
+ The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages
How does the Final Average work?
The Final Average is the arithmetic average calculated from all currently available final grades in the given semester.\n\nThe calculation scheme consists of the following steps:\n1. Summing up the final grades given by teachers\n2. Divide by the number of subjects that have already been graded
Final average
From e9ba65f8f67e0afab0a1f27740abb863810cbac2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sat, 18 Jun 2022 12:12:21 +0200
Subject: [PATCH 146/669] Fix multiline address in student info (#1891)
---
app/src/main/res/layout/fragment_student_info.xml | 4 ++--
app/src/main/res/layout/item_student_info.xml | 9 +++++----
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/app/src/main/res/layout/fragment_student_info.xml b/app/src/main/res/layout/fragment_student_info.xml
index d270da4a0..f72fed2ff 100644
--- a/app/src/main/res/layout/fragment_student_info.xml
+++ b/app/src/main/res/layout/fragment_student_info.xml
@@ -37,7 +37,7 @@
android:padding="10dp"
android:visibility="gone"
tools:ignore="UseCompoundDrawables"
- tools:visibility="visible">
+ tools:visibility="gone">
+ tools:visibility="gone">
+ android:focusable="true"
+ android:minHeight="64dp">
Date: Sun, 19 Jun 2022 21:04:05 +0200
Subject: [PATCH 147/669] Fix jumping point in notes on refresh (#1898)
---
app/src/main/res/layout/fragment_note.xml | 4 +++-
app/src/main/res/layout/item_note.xml | 2 +-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/app/src/main/res/layout/fragment_note.xml b/app/src/main/res/layout/fragment_note.xml
index f62a11a64..969003cc0 100644
--- a/app/src/main/res/layout/fragment_note.xml
+++ b/app/src/main/res/layout/fragment_note.xml
@@ -19,7 +19,9 @@
+ android:layout_height="match_parent"
+ tools:itemCount="4"
+ tools:listitem="@layout/item_note" />
Date: Thu, 23 Jun 2022 12:09:25 +0000
Subject: [PATCH 148/669] Bump coroutines from 1.6.2 to 1.6.3 (#1902)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index e629134e0..7c8046dad 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -177,7 +177,7 @@ ext {
room = "2.4.2"
chucker = "3.5.2"
mockk = "1.12.4"
- coroutines = "1.6.2"
+ coroutines = "1.6.3"
}
dependencies {
From c5dfea788c1a492b3cf06f4d00c99ac1fb4899a2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 23 Jun 2022 12:09:50 +0000
Subject: [PATCH 149/669] Bump annotation from 1.3.0 to 1.4.0 (#1900)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 7c8046dad..3e4912063 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -193,7 +193,7 @@ dependencies {
implementation "androidx.activity:activity-ktx:1.4.0"
implementation "androidx.appcompat:appcompat:1.4.2"
implementation "androidx.fragment:fragment-ktx:1.4.1"
- implementation "androidx.annotation:annotation:1.3.0"
+ implementation "androidx.annotation:annotation:1.4.0"
implementation "androidx.preference:preference-ktx:1.2.0"
implementation "androidx.recyclerview:recyclerview:1.2.1"
From 0fb55bd6c64c78f1b4e110c444f84c9094a27089 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sun, 26 Jun 2022 12:12:11 +0200
Subject: [PATCH 150/669] Fix doubled announcements (#1897)
---
.../49.json | 2445 +++++++++++++++++
.../github/wulkanowy/data/db/AppDatabase.kt | 3 +-
.../data/db/dao/SchoolAnnouncementDao.kt | 4 +-
.../data/db/entities/SchoolAnnouncement.kt | 4 +-
.../data/db/migrations/Migration49.kt | 23 +
.../data/mappers/DirectorInformationMapper.kt | 2 +-
.../SchoolAnnouncementRepository.kt | 7 +-
.../sync/works/SchoolAnnouncementWork.kt | 12 +-
.../notification/mock/schoolAnnouncement.kt | 2 +-
9 files changed, 2488 insertions(+), 14 deletions(-)
create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json
create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json
new file mode 100644
index 000000000..5472fb78a
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json
@@ -0,0 +1,2445 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 49,
+ "identityHash": "790d4dc0e11f38349c49af85fabf9b7b",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "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"
+ ],
+ "orders": [],
+ "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}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "kindergartenDiaryId",
+ "columnName": "kindergarten_diary_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "kindergarten_diary_id",
+ "semester_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "recipient",
+ "columnName": "recipient_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "removed",
+ "columnName": "removed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+ "fields": [
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "oneDriveId",
+ "columnName": "one_drive_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "real_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ReportingUnits",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shortName",
+ "columnName": "short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderName",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roles",
+ "columnName": "roles",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceId",
+ "columnName": "device_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "destination",
+ "columnName": "destination",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'"
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDismissible",
+ "columnName": "is_dismissible",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "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, '790d4dc0e11f38349c49af85fabf9b7b')"
+ ]
+ }
+}
\ No newline at end of file
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 379b8738f..17fd7d696 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
@@ -55,7 +55,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
- const val VERSION_SCHEMA = 48
+ const val VERSION_SCHEMA = 49
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
@@ -102,6 +102,7 @@ abstract class AppDatabase : RoomDatabase() {
Migration43(),
Migration44(),
Migration46(),
+ Migration49()
)
fun newInstance(
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt
index 15655f4ae..c32e4aba3 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/SchoolAnnouncementDao.kt
@@ -10,6 +10,6 @@ import javax.inject.Singleton
@Singleton
interface SchoolAnnouncementDao : BaseDao {
- @Query("SELECT * FROM SchoolAnnouncements WHERE student_id = :studentId ORDER BY date DESC")
- fun loadAll(studentId: Int): Flow>
+ @Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :userLoginId ORDER BY date DESC")
+ fun loadAll(userLoginId: Int): Flow>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt
index c8731bded..25e27ef18 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt
@@ -9,8 +9,8 @@ import java.time.LocalDate
@Entity(tableName = "SchoolAnnouncements")
data class SchoolAnnouncement(
- @ColumnInfo(name = "student_id")
- val studentId: Int,
+ @ColumnInfo(name = "user_login_id")
+ val userLoginId: Int,
val date: LocalDate,
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt
new file mode 100644
index 000000000..6e1de19d4
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration49.kt
@@ -0,0 +1,23 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration49 : Migration(48, 49) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("DROP TABLE IF EXISTS SchoolAnnouncements")
+
+ database.execSQL(
+ """
+ CREATE TABLE IF NOT EXISTS `SchoolAnnouncements` (
+ `user_login_id` INTEGER NOT NULL,
+ `date` INTEGER NOT NULL,
+ `subject` TEXT NOT NULL,
+ `content` TEXT NOT NULL,
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ `is_notified` INTEGER NOT NULL)
+ """.trimIndent()
+ )
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt
index d059db816..16f1bbac0 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt
@@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformatio
fun List.mapToEntities(student: Student) = map {
SchoolAnnouncement(
- studentId = student.userLoginId,
+ userLoginId = student.userLoginId,
date = it.date,
subject = it.subject,
content = it.content,
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
index cf7ac86cd..4c42d092f 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
@@ -28,7 +28,8 @@ class SchoolAnnouncementRepository @Inject constructor(
fun getSchoolAnnouncements(
student: Student,
- forceRefresh: Boolean, notify: Boolean = false
+ forceRefresh: Boolean,
+ notify: Boolean = false
) = networkBoundResource(
mutex = saveFetchResultMutex,
isResultEmpty = { it.isEmpty() },
@@ -37,7 +38,7 @@ class SchoolAnnouncementRepository @Inject constructor(
it.isEmpty() || forceRefresh || isExpired
},
query = {
- schoolAnnouncementDb.loadAll(student.studentId)
+ schoolAnnouncementDb.loadAll(student.userLoginId)
},
fetch = {
sdk.init(student)
@@ -56,7 +57,7 @@ class SchoolAnnouncementRepository @Inject constructor(
)
fun getSchoolAnnouncementFromDatabase(student: Student): Flow> {
- return schoolAnnouncementDb.loadAll(student.studentId)
+ return schoolAnnouncementDb.loadAll(student.userLoginId)
}
suspend fun updateSchoolAnnouncement(schoolAnnouncement: List) =
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt
index 805ceb3e4..1aedc8399 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/SchoolAnnouncementWork.kt
@@ -6,6 +6,7 @@ import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification
import kotlinx.coroutines.flow.first
+import java.time.LocalDate
import javax.inject.Inject
class SchoolAnnouncementWork @Inject constructor(
@@ -20,10 +21,13 @@ class SchoolAnnouncementWork @Inject constructor(
notify = notify,
).waitForResult()
-
- schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student).first()
- .filter { !it.isNotified }.let {
- if (it.isNotEmpty()) newSchoolAnnouncementNotification.notify(it, student)
+ schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student)
+ .first()
+ .filter { !it.isNotified && it.date >= LocalDate.now() }
+ .let {
+ if (it.isNotEmpty()) {
+ newSchoolAnnouncementNotification.notify(it, student)
+ }
schoolAnnouncementRepository.updateSchoolAnnouncement(it.onEach { schoolAnnouncement ->
schoolAnnouncement.isNotified = true
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt
index 9b21f08e6..e2dc5cd84 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/schoolAnnouncement.kt
@@ -19,6 +19,6 @@ val debugSchoolAnnouncementItems = listOf(
private fun generateAnnouncement(subject: String, content: String) = SchoolAnnouncement(
subject = subject,
content = content,
- studentId = 0,
+ userLoginId = 0,
date = LocalDate.now()
)
From c808bf2e616eb3abf36c21f22a3e022768469ed5 Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Sun, 26 Jun 2022 13:06:43 +0200
Subject: [PATCH 151/669] Fix timetable widget day reset (#1862)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Rafał Borcz
---
.../TimetableWidgetConfigureActivity.kt | 6 ++---
.../TimetableWidgetProvider.kt | 24 ++++++++++++++-----
2 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt
index a27dba882..6ef6cfc98 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetablewidget/TimetableWidgetConfigureActivity.kt
@@ -1,8 +1,6 @@
package io.github.wulkanowy.ui.modules.timetablewidget
-import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
-import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
-import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
+import android.appwidget.AppWidgetManager.*
import android.content.Intent
import android.os.Build
import android.os.Bundle
@@ -17,6 +15,7 @@ import io.github.wulkanowy.databinding.ActivityWidgetConfigureBinding
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.WidgetConfigureAdapter
import io.github.wulkanowy.ui.modules.login.LoginActivity
+import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_CONFIGURE
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER
import io.github.wulkanowy.utils.AppInfo
import javax.inject.Inject
@@ -92,6 +91,7 @@ class TimetableWidgetConfigureActivity :
.apply {
action = ACTION_APPWIDGET_UPDATE
putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId))
+ putExtra(EXTRA_FROM_CONFIGURE, true)
})
}
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 745769864..3ba2ae946 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
@@ -60,6 +60,8 @@ class TimetableWidgetProvider : BroadcastReceiver() {
private const val BUTTON_RESET = "buttonReset"
+ const val EXTRA_FROM_CONFIGURE = "extraFromConfigure"
+
const val EXTRA_FROM_PROVIDER = "extraFromProvider"
fun getDateWidgetKey(appWidgetId: Int) = "timetable_widget_date_$appWidgetId"
@@ -86,12 +88,22 @@ class TimetableWidgetProvider : BroadcastReceiver() {
}
private suspend fun onUpdate(context: Context, intent: Intent) {
- if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) {
- intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId ->
+ if (intent.getStringExtra(EXTRA_BUTTON_TYPE) == null) {
+ val isFromConfigure = intent.getBooleanExtra(EXTRA_FROM_CONFIGURE, false)
+ val appWidgetIds = intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS) ?: return
+
+ appWidgetIds.forEach { appWidgetId ->
val student =
getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)
+ val savedDataEpochDay = sharedPref.getLong(getDateWidgetKey(appWidgetId), 0)
- updateWidget(context, appWidgetId, getWidgetDateToLoad(appWidgetId), student)
+ val dateToLoad = if (isFromConfigure && savedDataEpochDay != 0L) {
+ LocalDate.ofEpochDay(savedDataEpochDay)
+ } else {
+ getWidgetDefaultDateToLoad(appWidgetId)
+ }
+
+ updateWidget(context, appWidgetId, dateToLoad, student)
}
} else {
val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE)
@@ -103,10 +115,10 @@ class TimetableWidgetProvider : BroadcastReceiver() {
val savedDate =
LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0))
val date = when (buttonType) {
- BUTTON_RESET -> getWidgetDateToLoad(toggledWidgetId)
+ BUTTON_RESET -> getWidgetDefaultDateToLoad(toggledWidgetId)
BUTTON_NEXT -> savedDate.nextSchoolDay
BUTTON_PREV -> savedDate.previousSchoolDay
- else -> getWidgetDateToLoad(toggledWidgetId)
+ else -> getWidgetDefaultDateToLoad(toggledWidgetId)
}
if (!buttonType.isNullOrBlank()) {
analytics.logEvent(
@@ -271,7 +283,7 @@ class TimetableWidgetProvider : BroadcastReceiver() {
return avatarBitmap
}
- private fun getWidgetDateToLoad(appWidgetId: Int): LocalDate {
+ private fun getWidgetDefaultDateToLoad(appWidgetId: Int): LocalDate {
val lastLessonEndTimestamp =
sharedPref.getLong(getTodayLastLessonEndDateTimeWidgetKey(appWidgetId), 0)
val lastLessonEndDateTime =
From d8f644c5b4081d44e76a53a60067b31cb7baadf5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sun, 26 Jun 2022 13:28:35 +0200
Subject: [PATCH 152/669] Add ads to dashboard (#1815)
---
app/build.gradle | 3 +
.../io/github/wulkanowy/utils/AdsHelper.kt | 28 +++++++
.../io/github/wulkanowy/utils/AdsHelper.kt | 28 +++++++
.../java/io/github/wulkanowy/WulkanowyApp.kt | 4 +
.../repositories/PreferencesRepository.kt | 38 +++++++--
.../ui/modules/dashboard/DashboardFragment.kt | 9 +++
.../ui/modules/dashboard/DashboardItem.kt | 23 +++---
.../dashboard/DashboardItemMoveCallback.kt | 3 +-
.../modules/dashboard/DashboardPresenter.kt | 35 +++++++-
.../ui/modules/dashboard/DashboardView.kt | 4 +-
.../{ => adapters}/DashboardAdapter.kt | 48 ++++++-----
.../DashboardAnnouncementsAdapter.kt | 4 +-
.../DashboardConferencesAdapter.kt | 4 +-
.../{ => adapters}/DashboardExamsAdapter.kt | 4 +-
.../{ => adapters}/DashboardGradesAdapter.kt | 2 +-
.../DashboardHomeworkAdapter.kt | 4 +-
.../wulkanowy/ui/modules/main/MainActivity.kt | 46 +++++++++++
.../ui/modules/main/MainPresenter.kt | 63 ++++++++++++---
.../wulkanowy/ui/modules/main/MainView.kt | 6 ++
.../wulkanowy/utils/ContextExtension.kt | 1 +
.../main/res/layout/dialog_ads_consent.xml | 79 +++++++++++++++++++
.../main/res/layout/item_dashboard_ads.xml | 15 ++++
.../main/res/values/preferences_defaults.xml | 2 +
app/src/main/res/values/preferences_keys.xml | 4 +
app/src/main/res/values/strings.xml | 7 ++
.../ui/modules/settings/ads/AdsFragment.kt | 70 ++++++++++++++--
.../ui/modules/settings/ads/AdsPresenter.kt | 64 ++++++++++++---
.../ui/modules/settings/ads/AdsView.kt | 8 +-
.../io/github/wulkanowy/utils/AdsHelper.kt | 59 ++++++++++++--
.../play/res/xml/scheme_preferences_ads.xml | 27 ++++++-
.../ui/modules/main/MainPresenterTest.kt | 14 +++-
31 files changed, 621 insertions(+), 85 deletions(-)
create mode 100644 app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt
create mode 100644 app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt
rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardAdapter.kt (96%)
rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardAnnouncementsAdapter.kt (95%)
rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardConferencesAdapter.kt (95%)
rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardExamsAdapter.kt (97%)
rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardGradesAdapter.kt (96%)
rename app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/{ => adapters}/DashboardHomeworkAdapter.kt (97%)
create mode 100644 app/src/main/res/layout/dialog_ads_consent.xml
create mode 100644 app/src/main/res/layout/item_dashboard_ads.xml
diff --git a/app/build.gradle b/app/build.gradle
index 3e4912063..29325abff 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -43,6 +43,7 @@ android {
}
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null"
+ buildConfigField "String", "DASHBOARD_TILE_AD_ID", "null"
if (System.env.SET_BUILD_TIMESTAMP) {
buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis())
@@ -99,6 +100,8 @@ android {
admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713"
]
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "\"${System.getenv("SINGLE_SUPPORT_AD_ID") ?: "ca-app-pub-3940256099942544/5354046379"}\""
+ buildConfigField "String", "DASHBOARD_TILE_AD_ID", "\"${System.getenv("DASHBOARD_TILE_AD_ID") ?: "ca-app-pub-3940256099942544/6300978111"}\""
+
}
fdroid {
diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt
new file mode 100644
index 000000000..461d29951
--- /dev/null
+++ b/app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt
@@ -0,0 +1,28 @@
+package io.github.wulkanowy.utils
+
+import android.content.Context
+import android.view.View
+import dagger.hilt.android.qualifiers.ApplicationContext
+import io.github.wulkanowy.data.repositories.PreferencesRepository
+import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
+import javax.inject.Inject
+
+@Suppress("unused")
+class AdsHelper @Inject constructor(
+ @ApplicationContext private val context: Context,
+ private val preferencesRepository: PreferencesRepository
+) {
+
+ fun initialize() {
+ preferencesRepository.isAdsEnabled = false
+ preferencesRepository.isAgreeToProcessData = false
+ preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS
+ }
+
+ @Suppress("RedundantSuspendModifier", "UNUSED_PARAMETER")
+ suspend fun getDashboardTileAdBanner(width: Int): AdBanner {
+ throw IllegalStateException("Can't get ad banner (F-droid)")
+ }
+}
+
+data class AdBanner(val view: View)
diff --git a/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt
new file mode 100644
index 000000000..0e9227022
--- /dev/null
+++ b/app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt
@@ -0,0 +1,28 @@
+package io.github.wulkanowy.utils
+
+import android.content.Context
+import android.view.View
+import dagger.hilt.android.qualifiers.ApplicationContext
+import io.github.wulkanowy.data.repositories.PreferencesRepository
+import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
+import javax.inject.Inject
+
+@Suppress("unused")
+class AdsHelper @Inject constructor(
+ @ApplicationContext private val context: Context,
+ private val preferencesRepository: PreferencesRepository
+) {
+
+ fun initialize() {
+ preferencesRepository.isAdsEnabled = false
+ preferencesRepository.isAgreeToProcessData = false
+ preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS
+ }
+
+ @Suppress("RedundantSuspendModifier", "UNUSED_PARAMETER")
+ suspend fun getDashboardTileAdBanner(width: Int): AdBanner {
+ throw IllegalStateException("Can't get ad banner (HMS)")
+ }
+}
+
+data class AdBanner(val view: View)
diff --git a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt
index b5103e3ec..7d2eeb1ec 100644
--- a/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt
+++ b/app/src/main/java/io/github/wulkanowy/WulkanowyApp.kt
@@ -31,10 +31,14 @@ class WulkanowyApp : Application(), Configuration.Provider {
@Inject
lateinit var analyticsHelper: AnalyticsHelper
+ @Inject
+ lateinit var adsHelper: AdsHelper
+
override fun onCreate() {
super.onCreate()
initializeAppLanguage()
themeManager.applyDefaultTheme()
+ adsHelper.initialize()
initLogging()
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
index 4cd85586f..237fb1a0a 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
@@ -222,16 +222,14 @@ class PreferencesRepository @Inject constructor(
get() = selectedDashboardTilesPreference.asFlow()
.map { set ->
set.map { DashboardItem.Tile.valueOf(it) }
- .plus(DashboardItem.Tile.ACCOUNT)
- .plus(DashboardItem.Tile.ADMIN_MESSAGE)
+ .plus(listOf(DashboardItem.Tile.ACCOUNT, DashboardItem.Tile.ADMIN_MESSAGE))
.toSet()
}
var selectedDashboardTiles: Set
get() = selectedDashboardTilesPreference.get()
.map { DashboardItem.Tile.valueOf(it) }
- .plus(DashboardItem.Tile.ACCOUNT)
- .plus(DashboardItem.Tile.ADMIN_MESSAGE)
+ .plus(listOf(DashboardItem.Tile.ACCOUNT, DashboardItem.Tile.ADMIN_MESSAGE))
.toSet()
set(value) {
val filteredValue = value.filterNot { it == DashboardItem.Tile.ACCOUNT }
@@ -271,7 +269,33 @@ class PreferencesRepository @Inject constructor(
var isAppReviewDone: Boolean
get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false)
- set(value) = sharedPref.edit().putBoolean(PREF_KEY_IN_APP_REVIEW_DONE, value).apply()
+ set(value) = sharedPref.edit { putBoolean(PREF_KEY_IN_APP_REVIEW_DONE, value) }
+
+ var isAppSupportShown: Boolean
+ get() = sharedPref.getBoolean(PREF_KEY_APP_SUPPORT_SHOWN, false)
+ set(value) = sharedPref.edit { putBoolean(PREF_KEY_APP_SUPPORT_SHOWN, value) }
+
+ var isAgreeToProcessData: Boolean
+ get() = getBoolean(
+ R.string.pref_key_ads_consent_data_processing,
+ R.bool.pref_default_ads_consent_data_processing
+ )
+ set(value) = sharedPref.edit {
+ putBoolean(context.getString(R.string.pref_key_ads_consent_data_processing), value)
+ }
+
+ var isPersonalizedAdsEnabled: Boolean
+ get() = sharedPref.getBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, false)
+ set(value) = sharedPref.edit { putBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, value) }
+
+ var isAdsEnabled: Boolean
+ get() = getBoolean(
+ R.string.pref_key_ads_enabled,
+ R.bool.pref_default_ads_enabled
+ )
+ set(value) = sharedPref.edit {
+ putBoolean(context.getString(R.string.pref_key_ads_enabled), value)
+ }
private fun getLong(id: Int, default: Int) = getLong(context.getString(id), default)
@@ -301,6 +325,10 @@ class PreferencesRepository @Inject constructor(
private const val PREF_KEY_IN_APP_REVIEW_DONE = "in_app_review_done"
+ private const val PREF_KEY_APP_SUPPORT_SHOWN = "app_support_shown"
+
+ private const val PREF_KEY_PERSONALIZED_ADS_ENABLED = "personalized_ads_enabled"
+
private const val PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS = "admin_message_dismissed_ids"
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
index 65832bdb1..de0b4a6c9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
@@ -18,6 +18,7 @@ import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
+import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter
import io.github.wulkanowy.ui.modules.exam.ExamFragment
import io.github.wulkanowy.ui.modules.grade.GradeFragment
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
@@ -47,6 +48,14 @@ class DashboardFragment : BaseFragment(R.layout.fragme
override var subtitleString =
LocalDate.now().toFormattedString("EEEE, d MMMM yyyy").capitalise()
+ override val tileWidth: Int
+ get() {
+ val recyclerWidth = binding.dashboardRecycler.width
+ val margin = requireContext().dpToPx(24f).toInt()
+
+ return ((recyclerWidth - margin) / resources.displayMetrics.density).toInt()
+ }
+
companion object {
fun newInstance() = DashboardFragment()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt
index c20bae7fa..e220ae236 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt
@@ -1,13 +1,9 @@
package io.github.wulkanowy.ui.modules.dashboard
-import io.github.wulkanowy.data.db.entities.AdminMessage
-import io.github.wulkanowy.data.db.entities.Conference
-import io.github.wulkanowy.data.db.entities.Exam
-import io.github.wulkanowy.data.db.entities.Grade
-import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
-import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.enums.GradeColorTheme
import io.github.wulkanowy.data.pojos.TimetableFull
+import io.github.wulkanowy.utils.AdBanner
import io.github.wulkanowy.data.db.entities.Homework as EntitiesHomework
sealed class DashboardItem(val type: Type) {
@@ -106,17 +102,26 @@ sealed class DashboardItem(val type: Type) {
override val isDataLoaded get() = conferences != null
}
+ data class Ads(
+ val adBanner: AdBanner? = null,
+ override val error: Throwable? = null,
+ override val isLoading: Boolean = false
+ ) : DashboardItem(Type.ADS) {
+
+ override val isDataLoaded get() = adBanner != null
+ }
+
enum class Type {
ADMIN_MESSAGE,
ACCOUNT,
HORIZONTAL_GROUP,
LESSONS,
+ ADS,
GRADES,
HOMEWORK,
ANNOUNCEMENTS,
EXAMS,
CONFERENCES,
- ADS
}
enum class Tile {
@@ -126,12 +131,12 @@ sealed class DashboardItem(val type: Type) {
MESSAGES,
ATTENDANCE,
LESSONS,
+ ADS,
GRADES,
HOMEWORK,
ANNOUNCEMENTS,
EXAMS,
CONFERENCES,
- ADS
}
}
@@ -148,4 +153,4 @@ fun DashboardItem.Tile.toDashboardItemType() = when (this) {
DashboardItem.Tile.EXAMS -> DashboardItem.Type.EXAMS
DashboardItem.Tile.CONFERENCES -> DashboardItem.Type.CONFERENCES
DashboardItem.Tile.ADS -> DashboardItem.Type.ADS
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt
index b9625570f..9c15acc35 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItemMoveCallback.kt
@@ -2,7 +2,8 @@ package io.github.wulkanowy.ui.modules.dashboard
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
-import java.util.Collections
+import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter
+import java.util.*
class DashboardItemMoveCallback(
private val dashboardAdapter: DashboardAdapter,
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index c33955bc7..e963a0205 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -8,6 +8,7 @@ import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.repositories.*
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
+import io.github.wulkanowy.utils.AdsHelper
import io.github.wulkanowy.utils.calculatePercentage
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import kotlinx.coroutines.flow.*
@@ -31,7 +32,8 @@ class DashboardPresenter @Inject constructor(
private val conferenceRepository: ConferenceRepository,
private val preferencesRepository: PreferencesRepository,
private val schoolAnnouncementRepository: SchoolAnnouncementRepository,
- private val adminMessageRepository: AdminMessageRepository
+ private val adminMessageRepository: AdminMessageRepository,
+ private val adsHelper: AdsHelper
) : BasePresenter(errorHandler, studentRepository) {
private val dashboardItemLoadedList = mutableListOf()
@@ -166,7 +168,7 @@ class DashboardPresenter @Inject constructor(
DashboardItem.Type.CONFERENCES -> {
loadConferences(student, forceRefresh)
}
- DashboardItem.Type.ADS -> TODO()
+ DashboardItem.Type.ADS -> loadAds(forceRefresh)
DashboardItem.Type.ADMIN_MESSAGE -> loadAdminMessage(student, forceRefresh)
}
}
@@ -595,6 +597,23 @@ class DashboardPresenter @Inject constructor(
.launchWithUniqueRefreshJob("dashboard_admin_messages", forceRefresh)
}
+ private fun loadAds(forceRefresh: Boolean) {
+ presenterScope.launch {
+ if (!forceRefresh) {
+ updateData(DashboardItem.Ads(), forceRefresh)
+ }
+
+ val dashboardAdItem =
+ runCatching {
+ DashboardItem.Ads(adsHelper.getDashboardTileAdBanner(view!!.tileWidth))
+ }
+ .onFailure { Timber.e(it) }
+ .getOrElse { DashboardItem.Ads(error = it) }
+
+ updateData(dashboardAdItem, forceRefresh)
+ }
+ }
+
private fun updateData(dashboardItem: DashboardItem, forceRefresh: Boolean) {
val isForceRefreshError = forceRefresh && dashboardItem.error != null
val isFirstRunDataLoadedError =
@@ -619,6 +638,18 @@ class DashboardPresenter @Inject constructor(
}
}
+ if (dashboardItem is DashboardItem.Ads) {
+ if (!dashboardItem.isDataLoaded) {
+ dashboardItemsToLoad = dashboardItemsToLoad - DashboardItem.Type.ADS
+ dashboardTileLoadedList = dashboardTileLoadedList - DashboardItem.Tile.ADS
+
+ dashboardItemLoadedList.removeAll { it.type == DashboardItem.Type.ADS }
+ } else {
+ dashboardItemsToLoad = dashboardItemsToLoad + DashboardItem.Type.ADS
+ dashboardTileLoadedList = dashboardTileLoadedList + DashboardItem.Tile.ADS
+ }
+ }
+
if (forceRefresh) {
updateForceRefreshData(dashboardItem)
} else {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt
index 2cc2f1d2d..767885434 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardView.kt
@@ -4,6 +4,8 @@ import io.github.wulkanowy.ui.base.BaseView
interface DashboardView : BaseView {
+ val tileWidth: Int
+
fun initView()
fun updateData(data: List)
@@ -27,4 +29,4 @@ interface DashboardView : BaseView {
fun openNotificationsCenterView()
fun openInternetBrowser(url: String)
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt
similarity index 96%
rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt
rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt
index 9191d43c9..a3c423a8b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.ui.modules.dashboard
+package io.github.wulkanowy.ui.modules.dashboard.adapters
import android.annotation.SuppressLint
import android.content.res.ColorStateList
@@ -22,24 +22,15 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.entities.TimetableHeader
import io.github.wulkanowy.data.enums.GradeColorTheme
-import io.github.wulkanowy.databinding.ItemDashboardAccountBinding
-import io.github.wulkanowy.databinding.ItemDashboardAdminMessageBinding
-import io.github.wulkanowy.databinding.ItemDashboardAnnouncementsBinding
-import io.github.wulkanowy.databinding.ItemDashboardConferencesBinding
-import io.github.wulkanowy.databinding.ItemDashboardExamsBinding
-import io.github.wulkanowy.databinding.ItemDashboardGradesBinding
-import io.github.wulkanowy.databinding.ItemDashboardHomeworkBinding
-import io.github.wulkanowy.databinding.ItemDashboardHorizontalGroupBinding
-import io.github.wulkanowy.databinding.ItemDashboardLessonsBinding
-import io.github.wulkanowy.utils.createNameInitialsDrawable
-import io.github.wulkanowy.utils.dpToPx
-import io.github.wulkanowy.utils.getThemeAttrColor
-import io.github.wulkanowy.utils.left
-import io.github.wulkanowy.utils.nickOrName
-import io.github.wulkanowy.utils.toFormattedString
+import io.github.wulkanowy.databinding.*
+import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
+import io.github.wulkanowy.utils.*
import timber.log.Timber
-import java.time.*
-import java.util.Timer
+import java.time.Duration
+import java.time.Instant
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.util.*
import javax.inject.Inject
import kotlin.concurrent.timer
@@ -120,6 +111,9 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter AdminMessageViewHolder(
ItemDashboardAdminMessageBinding.inflate(inflater, parent, false)
)
+ DashboardItem.Type.ADS.ordinal -> AdsViewHolder(
+ ItemDashboardAdsBinding.inflate(inflater, parent, false)
+ )
else -> throw IllegalArgumentException()
}
}
@@ -135,6 +129,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter bindExamsViewHolder(holder, position)
is ConferencesViewHolder -> bindConferencesViewHolder(holder, position)
is AdminMessageViewHolder -> bindAdminMessage(holder, position)
+ is AdsViewHolder -> bindAdsViewHolder(holder, position)
}
}
@@ -746,6 +741,20 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter,
private val oldList: List
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAnnouncementsAdapter.kt
similarity index 95%
rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt
rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAnnouncementsAdapter.kt
index 7a4c2b257..da5880153 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardAnnouncementsAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAnnouncementsAdapter.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.ui.modules.dashboard
+package io.github.wulkanowy.ui.modules.dashboard.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
@@ -33,4 +33,4 @@ class DashboardAnnouncementsAdapter :
class ViewHolder(val binding: SubitemDashboardAnnouncementsBinding) :
RecyclerView.ViewHolder(binding.root)
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardConferencesAdapter.kt
similarity index 95%
rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt
rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardConferencesAdapter.kt
index 64cf599c8..1244ff60f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardConferencesAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardConferencesAdapter.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.ui.modules.dashboard
+package io.github.wulkanowy.ui.modules.dashboard.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
@@ -33,4 +33,4 @@ class DashboardConferencesAdapter :
class ViewHolder(val binding: SubitemDashboardConferencesBinding) :
RecyclerView.ViewHolder(binding.root)
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardExamsAdapter.kt
similarity index 97%
rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt
rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardExamsAdapter.kt
index 060f224b3..583bf29da 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardExamsAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardExamsAdapter.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.ui.modules.dashboard
+package io.github.wulkanowy.ui.modules.dashboard.adapters
import android.annotation.SuppressLint
import android.view.LayoutInflater
@@ -56,4 +56,4 @@ class DashboardExamsAdapter :
class ViewHolder(val binding: SubitemDashboardExamsBinding) :
RecyclerView.ViewHolder(binding.root)
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt
similarity index 96%
rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt
rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt
index afffcc511..d00df9d41 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardGradesAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardGradesAdapter.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.ui.modules.dashboard
+package io.github.wulkanowy.ui.modules.dashboard.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardHomeworkAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardHomeworkAdapter.kt
similarity index 97%
rename from app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardHomeworkAdapter.kt
rename to app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardHomeworkAdapter.kt
index 55ec90294..8105ff9c8 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardHomeworkAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardHomeworkAdapter.kt
@@ -1,4 +1,4 @@
-package io.github.wulkanowy.ui.modules.dashboard
+package io.github.wulkanowy.ui.modules.dashboard.adapters
import android.annotation.SuppressLint
import android.view.LayoutInflater
@@ -53,4 +53,4 @@ class DashboardHomeworkAdapter : RecyclerView.Adapter(), MainVie
inAppReviewHelper.showInAppReview(this)
}
+ override fun showAppSupport() {
+ MaterialAlertDialogBuilder(this)
+ .setTitle(R.string.main_support_title)
+ .setMessage(R.string.main_support_description)
+ .setPositiveButton(R.string.main_support_positive) { _, _ -> presenter.onEnableAdsSelected() }
+ .setNegativeButton(android.R.string.cancel) { _, _ -> }
+ .setOnDismissListener { }
+ .show()
+ }
+
+ override fun showPrivacyPolicyDialog() {
+ val dialogAdsConsentBinding = DialogAdsConsentBinding.inflate(layoutInflater)
+
+ val dialog = MaterialAlertDialogBuilder(this)
+ .setTitle(R.string.pref_ads_consent_title)
+ .setMessage(R.string.pref_ads_consent_description)
+ .setView(dialogAdsConsentBinding.root)
+ .show()
+
+ dialogAdsConsentBinding.adsConsentOver.setOnCheckedChangeListener { _, isChecked ->
+ dialogAdsConsentBinding.adsConsentPersonalised.isEnabled = isChecked
+ }
+
+ dialogAdsConsentBinding.adsConsentPersonalised.setOnClickListener {
+ presenter.onPrivacyAgree(true)
+ dialog.dismiss()
+ }
+
+ dialogAdsConsentBinding.adsConsentNonPersonalised.setOnClickListener {
+ presenter.onPrivacyAgree(false)
+ dialog.dismiss()
+ }
+
+ dialogAdsConsentBinding.adsConsentPrivacy.setOnClickListener { presenter.onPrivacySelected() }
+ dialogAdsConsentBinding.adsConsentCancel.setOnClickListener { dialog.cancel() }
+ }
+
+ override fun openPrivacyPolicy() {
+ openInternetBrowser(
+ "https://wulkanowy.github.io/polityka-prywatnosci.html",
+ ::showMessage
+ )
+ }
+
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
navController.onSaveInstanceState(outState)
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 a07bdb371..8f457d925 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
@@ -14,11 +14,15 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.account.AccountView
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsView
+import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.ui.modules.grade.GradeView
import io.github.wulkanowy.ui.modules.message.MessageView
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersView
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
+import io.github.wulkanowy.utils.AdsHelper
import io.github.wulkanowy.utils.AnalyticsHelper
+import io.github.wulkanowy.utils.AppInfo
+import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import timber.log.Timber
@@ -29,10 +33,12 @@ import javax.inject.Inject
class MainPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
- private val prefRepository: PreferencesRepository,
+ private val preferencesRepository: PreferencesRepository,
private val syncManager: SyncManager,
private val analytics: AnalyticsHelper,
- private val json: Json
+ private val json: Json,
+ private val adsHelper: AdsHelper,
+ private val appInfo: AppInfo
) : BasePresenter(errorHandler, studentRepository) {
private var studentsWitSemesters: List? = null
@@ -47,7 +53,7 @@ class MainPresenter @Inject constructor(
private val Destination?.startMenuIndex
get() = when {
- this == null -> prefRepository.startMenuIndex
+ this == null -> preferencesRepository.startMenuIndex
destinationType in rootDestinationTypeList -> {
rootDestinationTypeList.indexOf(destinationType)
}
@@ -71,6 +77,8 @@ class MainPresenter @Inject constructor(
syncManager.startPeriodicSyncWorker()
+ checkAppSupport()
+
analytics.logEvent("app_open", "destination" to initDestination.toString())
Timber.i("Main view was initialized with $initDestination")
}
@@ -155,18 +163,53 @@ class MainPresenter @Inject constructor(
} == true
}
- private fun checkInAppReview() {
- prefRepository.inAppReviewCount++
+ fun onEnableAdsSelected() {
+ view?.showPrivacyPolicyDialog()
+ }
- if (prefRepository.inAppReviewDate == null) {
- prefRepository.inAppReviewDate = Instant.now()
+ fun onPrivacyAgree(isPersonalizedAds: Boolean) {
+ preferencesRepository.isAdsEnabled = true
+ preferencesRepository.isAgreeToProcessData = true
+ preferencesRepository.isPersonalizedAdsEnabled = isPersonalizedAds
+
+ adsHelper.initialize()
+
+ preferencesRepository.selectedDashboardTiles += DashboardItem.Tile.ADS
+ }
+
+ fun onPrivacySelected() {
+ view?.openPrivacyPolicy()
+ }
+
+ private fun checkInAppReview() {
+ preferencesRepository.inAppReviewCount++
+
+ if (preferencesRepository.inAppReviewDate == null) {
+ preferencesRepository.inAppReviewDate = Instant.now()
}
- if (!prefRepository.isAppReviewDone && prefRepository.inAppReviewCount >= 50 &&
- Instant.now().minus(Duration.ofDays(14)).isAfter(prefRepository.inAppReviewDate)
+ if (!preferencesRepository.isAppReviewDone && preferencesRepository.inAppReviewCount >= 50 &&
+ Instant.now().minus(Duration.ofDays(14)).isAfter(preferencesRepository.inAppReviewDate)
) {
view?.showInAppReview()
- prefRepository.isAppReviewDone = true
+ preferencesRepository.isAppReviewDone = true
+ }
+ }
+
+ private fun checkAppSupport() {
+ if (!preferencesRepository.isAppSupportShown && !preferencesRepository.isAdsEnabled
+ && appInfo.buildFlavor == "play"
+ ) {
+ presenterScope.launch {
+ val student = runCatching { studentRepository.getCurrentStudent(false) }
+ .onFailure { Timber.e(it) }
+ .getOrElse { return@launch }
+
+ if (Instant.now().minus(Duration.ofDays(28)).isAfter(student.registrationDate)) {
+ view?.showAppSupport()
+ preferencesRepository.isAppSupportShown = 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 3a57fcc6b..3d018e3d6 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
@@ -41,6 +41,12 @@ interface MainView : BaseView {
fun showInAppReview()
+ fun showAppSupport()
+
+ fun showPrivacyPolicyDialog()
+
+ fun openPrivacyPolicy()
+
fun openMoreDestination(destination: Destination)
interface MainChildView {
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 323e1e477..dd91d36d4 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt
@@ -13,6 +13,7 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawable
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import androidx.core.graphics.drawable.toBitmap
+
@ColorInt
fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int {
val array = obtainStyledAttributes(null, intArrayOf(colorAttr))
diff --git a/app/src/main/res/layout/dialog_ads_consent.xml b/app/src/main/res/layout/dialog_ads_consent.xml
new file mode 100644
index 000000000..395101627
--- /dev/null
+++ b/app/src/main/res/layout/dialog_ads_consent.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_dashboard_ads.xml b/app/src/main/res/layout/item_dashboard_ads.xml
new file mode 100644
index 000000000..b75bb27e0
--- /dev/null
+++ b/app/src/main/res/layout/item_dashboard_ads.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/values/preferences_defaults.xml b/app/src/main/res/values/preferences_defaults.xml
index deeb36961..9c092ec5e 100644
--- a/app/src/main/res/values/preferences_defaults.xml
+++ b/app/src/main/res/values/preferences_defaults.xml
@@ -37,4 +37,6 @@
- GRADES
- ANNOUNCEMENTS
+ false
+ false
diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml
index 849d989ee..f29080b33 100644
--- a/app/src/main/res/values/preferences_keys.xml
+++ b/app/src/main/res/values/preferences_keys.xml
@@ -37,4 +37,8 @@
notifications_piggyback
notifications_piggyback_cancel_original
single_ad_support
+ ads_enabled
+ ads_privacy_policy
+ ads_consent_data_processing
+ ads_over_eighteen
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2ca516adf..db591309f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -85,6 +85,9 @@
Log in
Session expired
Session expired, log in again
+ Application support
+ Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time
+ Enable ads
@@ -715,6 +718,10 @@
Privacy policy
Ad is loading
Thank you for your support, come back later for more ads
+ Can we use your data to display ads?
+ You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details
+ Personalized ads
+ Non-personalized ads
Advanced
Appearance & Behavior
diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt
index 8d31928b7..48a6fc3ec 100644
--- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt
+++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt
@@ -2,12 +2,15 @@ package io.github.wulkanowy.ui.modules.settings.ads
import android.os.Bundle
import android.view.View
+import androidx.preference.CheckBoxPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.SwitchPreferenceCompat
import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
+import io.github.wulkanowy.databinding.DialogAdsConsentBinding
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.ui.base.ErrorDialog
import io.github.wulkanowy.ui.modules.main.MainView
@@ -36,6 +39,22 @@ class AdsFragment : PreferenceFragmentCompat(), MainView.TitledView, AdsView {
presenter.onWatchSingleAdSelected()
true
}
+
+ findPreference(getString(R.string.pref_key_ads_privacy_policy))?.setOnPreferenceClickListener {
+ presenter.onPrivacySelected()
+ true
+ }
+
+ findPreference(getString(R.string.pref_key_ads_consent_data_processing))
+ ?.setOnPreferenceChangeListener { _, newValue ->
+ presenter.onConsentSelected(newValue as Boolean)
+ true
+ }
+
+ findPreference(getString(R.string.pref_key_ads_enabled))?.setOnPreferenceChangeListener { _, newValue ->
+ presenter.onAddEnabled(newValue as Boolean)
+ true
+ }
}
override fun showAd(ad: RewardedInterstitialAd) {
@@ -45,13 +64,50 @@ class AdsFragment : PreferenceFragmentCompat(), MainView.TitledView, AdsView {
}
override fun showPrivacyPolicyDialog() {
- MaterialAlertDialogBuilder(requireContext())
- .setTitle(getString(R.string.pref_ads_privacy_title))
- .setMessage(getString(R.string.pref_ads_privacy_description))
- .setPositiveButton(getString(R.string.pref_ads_privacy_agree)) { _, _ -> presenter.onAgreedPrivacy() }
- .setNegativeButton(android.R.string.cancel) { _, _ -> }
- .setNeutralButton(getString(R.string.pref_ads_privacy_link)) { _, _ -> presenter.onPrivacySelected() }
+ val dialogAdsConsentBinding = DialogAdsConsentBinding.inflate(layoutInflater)
+
+ val dialog = MaterialAlertDialogBuilder(requireContext())
+ .setTitle(R.string.pref_ads_consent_title)
+ .setMessage(R.string.pref_ads_consent_description)
+ .setView(dialogAdsConsentBinding.root)
+ .setOnCancelListener { presenter.onPrivacyDialogCanceled() }
.show()
+
+ dialogAdsConsentBinding.adsConsentOver.setOnCheckedChangeListener { _, isChecked ->
+ dialogAdsConsentBinding.adsConsentPersonalised.isEnabled = isChecked
+ }
+
+ dialogAdsConsentBinding.adsConsentPersonalised.setOnClickListener {
+ presenter.onPersonalizedAgree()
+ dialog.dismiss()
+ }
+
+ dialogAdsConsentBinding.adsConsentNonPersonalised.setOnClickListener {
+ presenter.onNonPersonalizedAgree()
+ dialog.dismiss()
+ }
+
+ dialogAdsConsentBinding.adsConsentPrivacy.setOnClickListener { presenter.onPrivacySelected() }
+ dialogAdsConsentBinding.adsConsentCancel.setOnClickListener { dialog.cancel() }
+ }
+
+ override fun showProcessingDataSummary(isPersonalized: Boolean?) {
+ val summaryText = isPersonalized?.let {
+ getString(if (it) R.string.pref_ads_summary_personalized else R.string.pref_ads_summary_non_personalized)
+ }
+
+ findPreference(getString(R.string.pref_key_ads_consent_data_processing))
+ ?.summary = summaryText
+ }
+
+ override fun setCheckedProcessingData(checked: Boolean) {
+ findPreference(getString(R.string.pref_key_ads_consent_data_processing))
+ ?.isChecked = checked
+ }
+
+ override fun setCheckedAdsEnabled(checked: Boolean) {
+ findPreference(getString(R.string.pref_key_ads_enabled))
+ ?.isChecked = checked
}
override fun openPrivacyPolicy() {
@@ -98,4 +154,4 @@ class AdsFragment : PreferenceFragmentCompat(), MainView.TitledView, AdsView {
override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
}
-}
\ No newline at end of file
+}
diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt
index 5ccbce1e5..85c14c0a0 100644
--- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt
+++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt
@@ -1,8 +1,10 @@
package io.github.wulkanowy.ui.modules.settings.ads
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
+import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.utils.AdsHelper
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -11,24 +13,22 @@ import javax.inject.Inject
class AdsPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
- private val adsHelper: AdsHelper
+ private val adsHelper: AdsHelper,
+ private val preferencesRepository: PreferencesRepository
) : BasePresenter(errorHandler, studentRepository) {
override fun onAttachView(view: AdsView) {
super.onAttachView(view)
view.initView()
Timber.i("Settings ads view was initialized")
+
+ view.showProcessingDataSummary(
+ preferencesRepository.isPersonalizedAdsEnabled.takeIf {
+ preferencesRepository.isAgreeToProcessData
+ })
}
fun onWatchSingleAdSelected() {
- view?.showPrivacyPolicyDialog()
- }
-
- fun onPrivacySelected() {
- view?.openPrivacyPolicy()
- }
-
- fun onAgreedPrivacy() {
view?.showLoadingSupportAd(true)
presenterScope.launch {
runCatching { adsHelper.getSupportAd() }
@@ -41,4 +41,48 @@ class AdsPresenter @Inject constructor(
}
}
}
-}
\ No newline at end of file
+
+ fun onConsentSelected(isChecked: Boolean) {
+ if (isChecked) {
+ view?.showPrivacyPolicyDialog()
+ } else {
+ view?.showProcessingDataSummary(null)
+ view?.setCheckedAdsEnabled(false)
+ onAddEnabled(false)
+ }
+ }
+
+ fun onPrivacySelected() {
+ view?.openPrivacyPolicy()
+ }
+
+ fun onPrivacyDialogCanceled() {
+ view?.setCheckedProcessingData(false)
+ }
+
+ fun onNonPersonalizedAgree() {
+ preferencesRepository.isPersonalizedAdsEnabled = false
+
+ adsHelper.initialize()
+
+ view?.setCheckedProcessingData(true)
+ view?.showProcessingDataSummary(false)
+ }
+
+ fun onPersonalizedAgree() {
+ preferencesRepository.isPersonalizedAdsEnabled = true
+
+ adsHelper.initialize()
+
+ view?.setCheckedProcessingData(true)
+ view?.showProcessingDataSummary(true)
+ }
+
+ fun onAddEnabled(isEnabled: Boolean) {
+ if (isEnabled) {
+ preferencesRepository.selectedDashboardTiles += DashboardItem.Tile.ADS
+ } else {
+ preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS
+ }
+ }
+}
diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsView.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsView.kt
index 89de7bd1d..8de6e60d3 100644
--- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsView.kt
+++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsView.kt
@@ -16,4 +16,10 @@ interface AdsView : BaseView {
fun showLoadingSupportAd(show: Boolean)
fun showWatchAdOncePerVisit(show: Boolean)
-}
\ No newline at end of file
+
+ fun setCheckedAdsEnabled(checked: Boolean)
+
+ fun setCheckedProcessingData(checked: Boolean)
+
+ fun showProcessingDataSummary(isPersonalized: Boolean?)
+}
diff --git a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt
index dde4d0121..6be8e924c 100644
--- a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt
+++ b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt
@@ -2,27 +2,39 @@ package io.github.wulkanowy.utils
import android.content.Context
import android.os.Bundle
+import android.view.View
import com.google.ads.mediation.admob.AdMobAdapter
-import com.google.android.gms.ads.AdRequest
-import com.google.android.gms.ads.LoadAdError
-import com.google.android.gms.ads.MobileAds
+import com.google.android.gms.ads.*
import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd
import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAdLoadCallback
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.BuildConfig
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
-class AdsHelper @Inject constructor(@ApplicationContext private val context: Context) {
+
+class AdsHelper @Inject constructor(
+ @ApplicationContext private val context: Context,
+ private val preferencesRepository: PreferencesRepository
+) {
+
+ fun initialize() {
+ if (preferencesRepository.isAgreeToProcessData) {
+ MobileAds.initialize(context)
+ }
+ }
suspend fun getSupportAd(): RewardedInterstitialAd? {
- MobileAds.initialize(context)
-
val extra = Bundle().apply { putString("npa", "1") }
val adRequest = AdRequest.Builder()
- .addNetworkExtrasBundle(AdMobAdapter::class.java, extra)
+ .apply {
+ if (!preferencesRepository.isPersonalizedAdsEnabled) {
+ addNetworkExtrasBundle(AdMobAdapter::class.java, extra)
+ }
+ }
.build()
return suspendCoroutine {
@@ -41,4 +53,35 @@ class AdsHelper @Inject constructor(@ApplicationContext private val context: Con
})
}
}
-}
\ No newline at end of file
+
+ suspend fun getDashboardTileAdBanner(width: Int): AdBanner {
+ val extra = Bundle().apply { putString("npa", "1") }
+ val adRequest = AdRequest.Builder()
+ .apply {
+ if (!preferencesRepository.isPersonalizedAdsEnabled) {
+ addNetworkExtrasBundle(AdMobAdapter::class.java, extra)
+ }
+ }
+ .build()
+
+ return suspendCoroutine {
+ val adView = AdView(context).apply {
+ adSize = AdSize.getPortraitAnchoredAdaptiveBannerAdSize(context, width)
+ adUnitId = BuildConfig.DASHBOARD_TILE_AD_ID
+ adListener = object : AdListener() {
+ override fun onAdFailedToLoad(loadAdError: LoadAdError) {
+ it.resumeWithException(IllegalArgumentException(loadAdError.message))
+ }
+
+ override fun onAdLoaded() {
+ it.resume(AdBanner(this@apply))
+ }
+ }
+ }
+
+ adView.loadAd(adRequest)
+ }
+ }
+}
+
+data class AdBanner(val view: View)
diff --git a/app/src/play/res/xml/scheme_preferences_ads.xml b/app/src/play/res/xml/scheme_preferences_ads.xml
index 52a3df58d..d5e93a355 100644
--- a/app/src/play/res/xml/scheme_preferences_ads.xml
+++ b/app/src/play/res/xml/scheme_preferences_ads.xml
@@ -2,11 +2,34 @@
+ app:title="Agreements">
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt
index 720239e62..6cfab1995 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt
@@ -4,7 +4,9 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.ErrorHandler
+import io.github.wulkanowy.utils.AdsHelper
import io.github.wulkanowy.utils.AnalyticsHelper
+import io.github.wulkanowy.utils.AppInfo
import io.mockk.*
import io.mockk.impl.annotations.MockK
import kotlinx.serialization.json.Json
@@ -31,6 +33,12 @@ class MainPresenterTest {
@MockK(relaxed = true)
lateinit var analytics: AnalyticsHelper
+ @MockK(relaxed = true)
+ lateinit var appInfo: AppInfo
+
+ @MockK(relaxed = true)
+ lateinit var adsHelper: AdsHelper
+
private lateinit var presenter: MainPresenter
@Before
@@ -42,10 +50,12 @@ class MainPresenterTest {
presenter = MainPresenter(
errorHandler = errorHandler,
studentRepository = studentRepository,
- prefRepository = prefRepository,
+ preferencesRepository = prefRepository,
syncManager = syncManager,
analytics = analytics,
- json = Json
+ json = Json,
+ appInfo = appInfo,
+ adsHelper = adsHelper
)
presenter.onAttachView(mainView, null)
}
From c4689fcbb38f7055f5378d7c6a537a79841e3a96 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sun, 26 Jun 2022 12:44:06 +0000
Subject: [PATCH 153/669] Bump agconnect-crash from 1.6.6.200 to 1.7.0.300
(#1899)
---
app/build.gradle | 2 +-
app/src/debug/agconnect-services.json | 115 +++++++++++++++-----
app/src/release/agconnect-services.json.gpg | Bin 608 -> 972 bytes
build.gradle | 2 +-
4 files changed, 89 insertions(+), 30 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 29325abff..ab1d0aefd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -248,7 +248,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:21.0.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.5.0.300'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.6.200'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.0.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
diff --git a/app/src/debug/agconnect-services.json b/app/src/debug/agconnect-services.json
index 48192df01..52426f54e 100644
--- a/app/src/debug/agconnect-services.json
+++ b/app/src/debug/agconnect-services.json
@@ -1,33 +1,92 @@
{
- "agcgw":{
- "backurl":"connect-dre.dbankcloud.cn",
- "url":"connect-dre.hispace.hicloud.com"
- },
- "client":{
- "cp_id":"890048000024105546",
- "product_id":"",
- "client_id":"",
- "client_secret":"",
- "app_id":"101440411",
- "package_name":"io.github.wulkanowy.dev",
- "api_key":""
- },
- "service":{
- "analytics":{
- "collector_url":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn",
- "resource_id":"p1",
- "channel_id":""
- },
+ "agcgw": {
+ "backurl": "connect-dre.hispace.hicloud.com",
+ "url": "connect-dre.dbankcloud.cn",
+ "websocketbackurl": "connect-ws-dre.hispace.dbankcloud.com",
+ "websocketurl": "connect-ws-dre.hispace.dbankcloud.cn"
+ },
+ "agcgw_all": {
+ "CN": "connect-drcn.dbankcloud.cn",
+ "CN_back": "connect-drcn.hispace.hicloud.com",
+ "DE": "connect-dre.dbankcloud.cn",
+ "DE_back": "connect-dre.hispace.hicloud.com",
+ "RU": "connect-drru.hispace.dbankcloud.ru",
+ "RU_back": "connect-drru.hispace.dbankcloud.cn",
+ "SG": "connect-dra.dbankcloud.cn",
+ "SG_back": "connect-dra.hispace.hicloud.com"
+ },
+ "websocketgw_all": {
+ "CN": "connect-ws-drcn.hispace.dbankcloud.cn",
+ "CN_back": "connect-ws-drcn.hispace.dbankcloud.com",
+ "DE": "connect-ws-dre.hispace.dbankcloud.cn",
+ "DE_back": "connect-ws-dre.hispace.dbankcloud.com",
+ "RU": "connect-ws-drru.hispace.dbankcloud.ru",
+ "RU_back": "connect-ws-drru.hispace.dbankcloud.cn",
+ "SG": "connect-ws-dra.hispace.dbankcloud.cn",
+ "SG_back": "connect-ws-dra.hispace.dbankcloud.com"
+ },
+ "client": {
+ "cp_id": "890048000024105546",
+ "product_id": "736430079244736562",
+ "client_id": "514530959291319360",
+ "client_secret": "C42522DBF17D3D4BBE9D9C1783A54484B7E6844B388B7A67502D36A633A4186B",
+ "project_id": "736430079244736562",
+ "app_id": "106552551",
+ "api_key": "CgB6e3x9BUNiq+r8ebCHNojjjYsMT4pJSjjNDOkm9owtBb6rVI6LjnASoZBRxbjjhObcrV5gANo99fI/eKZDTbWS",
+ "package_name": "io.github.wulkanowy.dev"
+ },
+ "oauth_client": {
+ "client_id": "106552551",
+ "client_type": 1
+ },
+ "app_info": {
+ "app_id": "106552551",
+ "package_name": "io.github.wulkanowy.dev"
+ },
+ "service": {
+ "analytics": {
+ "collector_url": "datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn",
+ "collector_url_ru": "datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com",
+ "collector_url_sg": "datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn",
+ "collector_url_de": "datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn",
+ "collector_url_cn": "datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn",
+ "resource_id": "p1",
+ "channel_id": ""
+ },
"search":{
"url":"https://search-dre.cloud.huawei.com"
},
- "cloudstorage":{
- "storage_url":"https://ops-dre.agcstorage.link"
- },
- "ml":{
- "mlservice_url":"ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn"
- }
- },
- "region":"DE",
- "configuration_version":"1.0"
+ "cloudstorage": {
+ "storage_url_sg_back": "https://agc-storage-dra.cloud.huawei.asia",
+ "storage_url_ru_back": "https://agc-storage-drru.cloud.huawei.ru",
+ "storage_url_ru": "https://agc-storage-drru.cloud.huawei.ru",
+ "storage_url_de_back": "https://agc-storage-dre.cloud.huawei.eu",
+ "storage_url_de": "https://ops-dre.agcstorage.link",
+ "storage_url": "https://agc-storage-drcn.platform.dbankcloud.cn",
+ "storage_url_sg": "https://ops-dra.agcstorage.link",
+ "storage_url_cn_back": "https://agc-storage-drcn.cloud.huawei.com.cn",
+ "storage_url_cn": "https://agc-storage-drcn.platform.dbankcloud.cn"
+ },
+ "ml": {
+ "mlservice_url": "ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn"
+ }
+ },
+ "region": "DE",
+ "configuration_version": "3.0",
+ "appInfos": [
+ {
+ "package_name": "io.github.wulkanowy.dev",
+ "client": {
+ "app_id": "106552551"
+ },
+ "app_info": {
+ "package_name": "io.github.wulkanowy.dev",
+ "app_id": "106552551"
+ },
+ "oauth_client": {
+ "client_type": 1,
+ "client_id": "106552551"
+ }
+ }
+ ]
}
diff --git a/app/src/release/agconnect-services.json.gpg b/app/src/release/agconnect-services.json.gpg
index 484d8bc72d7a15b689a3fb76cddd91f1992cebf4..149b9b26818f97db4ac68202bbde3c1fde04f422 100644
GIT binary patch
literal 972
zcmeC-W#MFI%CvVWn)pF~)2B->8E2?+NK~+wEmA%j+80#$WVzsb?=#N){B2s#L*)Xl
zZftqWws;5gwPRWTW0pQkYnQlnt96a~2Ih|&t&Yz4^>Vsy1%Lha8+soXZ;TK;wAb-c
zrbc&sd2mi||2EUv+>Ym7Zc%gXTsGnJd1E!n9efv(w#Qj7esS%=ulUx5GvhiIxz#_P
zAa8$t|J~n{H%3hk+)<;rJ>W_7&Yi7mBc?8J-Bw@S<{#Nnd-zXuRmZmZPxzC=rd>DG
zt~}u2vEbt;!7KMp7@ZQk-uP{z2}AR24dvn+%J%|3UOd$m+kRe?tsiSI56
zGami;DkoQPrL^AiuG+U-!(Wc&MA)@%%}{p^851=%
zrr$g3pL@LHZlAr>@WN@kJ@Ow_I4zbIo3<9eIc%x@Xj&!5jQUq`|7{doV&wf#@kMD@
zG}q+tJvq1H@ACiQKY9MSolknG!ancx=JoR?>qkpYlT^ripBuN$E1e;Tb)iaU-O;nF
zKFpgPW$%=K=>FN$imm@!!XIinUKLEYx4R-MsOFS&udE<-)*0T92YyEExY57zP@&-F
zO`9Cv)OvRcU-Dihozx$pJ>}D-kjC6Q78zz`C7A+`-PfqRJ$ZHU=GN%2{QzB@UgFw)%4P>D`2a!wQ?_
z*t6rbZ}piinpM2|wokEc{NXCQMT%`=R?8yi-LUw{@AjehVSJr|T}orT%!4hCac3Cx
z4p%KtU8bn2ov@VA#qW1A$ByD7C&jiUU3GDL^X{z^EL66&iv3sFV(yXorruHL
z;FS4MCbdWI6g_k~z;f-%pF8^Anwt`f<4(-p*%JAahnJt7+wqHO!CVvlX6{+%yG1u0
zva4IZZGQcln_j21_lX|jEN{D%z0lypyr3&Po?X(Lf4cVH9K*Psyvnz^{@YGTo#R(E
z-)!YF*$SZrk{4~6qIHsHx~5$Y(qDPeNWG3LCrj>*&9acxT8
zenrbN!D{v|T*0w*XS%L^Sb9*_Tk4?jeV1R2JD1dM64L&9A>>KjrzZ`;kK~(Y{Jt+;
z)bDJ0?*Ej1c}}9y4D0U}&d9vu6Z%xe-qF&wPk++Yy*F;Zs*2Q7TXvaKwqe?>E3b?G
zdK9-XSUz(@jfchUcT{rL0@)a|GUkmJOb{FkC$JQ
z<9qd5f0pN$s^e^HO*OcBV|XsGG3abCtw>m8GjY=T)lWH+f*=1sbi*lPLHV{M=biou
zOO4-7_h*9V_;&zm=sJ!mvAJ
zv*7Qg%cn7C#@vW}SJ9r;-}HOaW}{FS^<({6qQ&2CW+=M^_A?7Hd^j4{7;^OKjISKR7LmZLtn6cQ|L6U~ur$r?+|kZ4&s-FIg{O8v5|;#tTV|E&Ch8nEZvEA51G>
zF!yZDzuEVEENhqdm7inF+p=lJozs`nSoU)+3l5pHXw5z|rgldYkJ)}+E2c5G2Bdgw
zz0~8>ILTT}KlYGWN?rQT$!WL$)~*a`F5Ta>kSltz^?%p-Nm54^bL|gZ(O44p
Date: Sun, 26 Jun 2022 15:50:35 +0200
Subject: [PATCH 154/669] Fix ads tile (#1905)
---
.../repositories/PreferencesRepository.kt | 25 ++++++++++++++++---
.../modules/dashboard/DashboardPresenter.kt | 6 ++++-
.../ui/modules/main/MainPresenter.kt | 4 +--
.../ui/modules/settings/ads/AdsFragment.kt | 5 ----
.../ui/modules/settings/ads/AdsPresenter.kt | 10 --------
.../io/github/wulkanowy/utils/AdsHelper.kt | 2 +-
6 files changed, 29 insertions(+), 23 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
index 237fb1a0a..486538e0c 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
@@ -222,17 +222,31 @@ class PreferencesRepository @Inject constructor(
get() = selectedDashboardTilesPreference.asFlow()
.map { set ->
set.map { DashboardItem.Tile.valueOf(it) }
- .plus(listOf(DashboardItem.Tile.ACCOUNT, DashboardItem.Tile.ADMIN_MESSAGE))
+ .plus(
+ listOfNotNull(
+ DashboardItem.Tile.ACCOUNT,
+ DashboardItem.Tile.ADMIN_MESSAGE,
+ DashboardItem.Tile.ADS.takeIf { isAdsEnabled }
+ )
+ )
.toSet()
}
var selectedDashboardTiles: Set
get() = selectedDashboardTilesPreference.get()
.map { DashboardItem.Tile.valueOf(it) }
- .plus(listOf(DashboardItem.Tile.ACCOUNT, DashboardItem.Tile.ADMIN_MESSAGE))
+ .plus(
+ listOfNotNull(
+ DashboardItem.Tile.ACCOUNT,
+ DashboardItem.Tile.ADMIN_MESSAGE,
+ DashboardItem.Tile.ADS.takeIf { isAdsEnabled }
+ )
+ )
.toSet()
set(value) {
- val filteredValue = value.filterNot { it == DashboardItem.Tile.ACCOUNT }
+ val filteredValue = value.filterNot {
+ it == DashboardItem.Tile.ACCOUNT || it == DashboardItem.Tile.ADMIN_MESSAGE
+ }
.map { it.name }
.toSet()
@@ -288,6 +302,11 @@ class PreferencesRepository @Inject constructor(
get() = sharedPref.getBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, false)
set(value) = sharedPref.edit { putBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, value) }
+ val isAdsEnabledFlow = flowSharedPref.getBoolean(
+ context.getString(R.string.pref_key_ads_enabled),
+ context.resources.getBoolean(R.bool.pref_default_ads_enabled)
+ ).asFlow()
+
var isAdsEnabled: Boolean
get() = getBoolean(
R.string.pref_key_ads_enabled,
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index e963a0205..5d7c7df4b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -57,7 +57,11 @@ class DashboardPresenter @Inject constructor(
showContent(false)
}
- preferencesRepository.selectedDashboardTilesFlow
+ merge(
+ preferencesRepository.selectedDashboardTilesFlow,
+ preferencesRepository.isAdsEnabledFlow
+ .map { preferencesRepository.selectedDashboardTiles }
+ )
.onEach { loadData(tilesToLoad = it) }
.launch("dashboard_pref")
}
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 8f457d925..9c32d8583 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
@@ -14,7 +14,6 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.account.AccountView
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsView
-import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.ui.modules.grade.GradeView
import io.github.wulkanowy.ui.modules.message.MessageView
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersView
@@ -168,13 +167,12 @@ class MainPresenter @Inject constructor(
}
fun onPrivacyAgree(isPersonalizedAds: Boolean) {
- preferencesRepository.isAdsEnabled = true
preferencesRepository.isAgreeToProcessData = true
preferencesRepository.isPersonalizedAdsEnabled = isPersonalizedAds
adsHelper.initialize()
- preferencesRepository.selectedDashboardTiles += DashboardItem.Tile.ADS
+ preferencesRepository.isAdsEnabled = true
}
fun onPrivacySelected() {
diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt
index 48a6fc3ec..de4c591e1 100644
--- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt
+++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsFragment.kt
@@ -50,11 +50,6 @@ class AdsFragment : PreferenceFragmentCompat(), MainView.TitledView, AdsView {
presenter.onConsentSelected(newValue as Boolean)
true
}
-
- findPreference(getString(R.string.pref_key_ads_enabled))?.setOnPreferenceChangeListener { _, newValue ->
- presenter.onAddEnabled(newValue as Boolean)
- true
- }
}
override fun showAd(ad: RewardedInterstitialAd) {
diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt
index 85c14c0a0..772d616d7 100644
--- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt
+++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt
@@ -4,7 +4,6 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.utils.AdsHelper
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -48,7 +47,6 @@ class AdsPresenter @Inject constructor(
} else {
view?.showProcessingDataSummary(null)
view?.setCheckedAdsEnabled(false)
- onAddEnabled(false)
}
}
@@ -77,12 +75,4 @@ class AdsPresenter @Inject constructor(
view?.setCheckedProcessingData(true)
view?.showProcessingDataSummary(true)
}
-
- fun onAddEnabled(isEnabled: Boolean) {
- if (isEnabled) {
- preferencesRepository.selectedDashboardTiles += DashboardItem.Tile.ADS
- } else {
- preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS
- }
- }
}
diff --git a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt
index 6be8e924c..c536e2218 100644
--- a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt
+++ b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt
@@ -66,7 +66,7 @@ class AdsHelper @Inject constructor(
return suspendCoroutine {
val adView = AdView(context).apply {
- adSize = AdSize.getPortraitAnchoredAdaptiveBannerAdSize(context, width)
+ setAdSize(AdSize.getPortraitAnchoredAdaptiveBannerAdSize(context, width))
adUnitId = BuildConfig.DASHBOARD_TILE_AD_ID
adListener = object : AdListener() {
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
From b70649f13672b45e8574cdc2f92c8ebc6299bfec Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 28 Jun 2022 15:05:51 +0000
Subject: [PATCH 155/669] Bump about_libraries from 10.3.0 to 10.3.1 (#1907)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 0391e282c..21964d49d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
buildscript {
ext {
kotlin_version = '1.7.0'
- about_libraries = '10.3.0'
+ about_libraries = '10.3.1'
hilt_version = "2.42"
}
repositories {
From e70fe6f097039ea0ddd8e92d96627d82ea6ac925 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 28 Jun 2022 15:06:20 +0000
Subject: [PATCH 156/669] Bump firebase-crashlytics-gradle from 2.9.0 to 2.9.1
(#1910)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 21964d49d..7949ceaac 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,7 +17,7 @@ buildscript {
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.huawei.agconnect:agcp:1.7.0.300'
- classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.0'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513"
From 0f11f14c3e3df07a3a77c7405df8245689831d12 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 28 Jun 2022 15:07:33 +0000
Subject: [PATCH 157/669] Bump firebase-bom from 30.1.0 to 30.2.0 (#1909)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index ab1d0aefd..b7a5c04b0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -239,7 +239,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.7.0'
- playImplementation platform('com.google.firebase:firebase-bom:30.1.0')
+ playImplementation platform('com.google.firebase:firebase-bom:30.2.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 1b74bffc06bab18be850a9a2be80a75385fb6166 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sat, 2 Jul 2022 19:10:57 +0200
Subject: [PATCH 158/669] Fix no mobile devices on parent account (#1896)
---
.../50.json | 2445 +++++++++++++++++
.../github/wulkanowy/data/db/AppDatabase.kt | 5 +-
.../wulkanowy/data/db/dao/MobileDeviceDao.kt | 2 +-
.../data/db/entities/MobileDevice.kt | 2 +-
.../data/db/migrations/Migration50.kt | 21 +
.../data/mappers/MobileDeviceMapper.kt | 6 +-
.../repositories/MobileDeviceRepository.kt | 4 +-
.../MobileDeviceRepositoryTest.kt | 20 +-
8 files changed, 2486 insertions(+), 19 deletions(-)
create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json
create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json
new file mode 100644
index 000000000..4361db954
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json
@@ -0,0 +1,2445 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 50,
+ "identityHash": "87455aae2b15baa976386c833afa9cd9",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "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"
+ ],
+ "orders": [],
+ "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}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "kindergartenDiaryId",
+ "columnName": "kindergarten_diary_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "kindergarten_diary_id",
+ "semester_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "recipient",
+ "columnName": "recipient_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "removed",
+ "columnName": "removed",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+ "fields": [
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "oneDriveId",
+ "columnName": "one_drive_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "real_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "ReportingUnits",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shortName",
+ "columnName": "short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderId",
+ "columnName": "sender_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "senderName",
+ "columnName": "sender_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roles",
+ "columnName": "roles",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "destination",
+ "columnName": "destination",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'"
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDismissible",
+ "columnName": "is_dismissible",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "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, '87455aae2b15baa976386c833afa9cd9')"
+ ]
+ }
+}
\ No newline at end of file
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 17fd7d696..87915a9ed 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
@@ -55,7 +55,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
- const val VERSION_SCHEMA = 49
+ const val VERSION_SCHEMA = 50
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
@@ -102,7 +102,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration43(),
Migration44(),
Migration46(),
- Migration49()
+ Migration49(),
+ Migration50()
)
fun newInstance(
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt
index 081e859a5..96382cc10 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MobileDeviceDao.kt
@@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow
@Dao
interface MobileDeviceDao : BaseDao {
- @Query("SELECT * FROM MobileDevices WHERE student_id = :userLoginId ORDER BY date DESC")
+ @Query("SELECT * FROM MobileDevices WHERE user_login_id = :userLoginId ORDER BY date DESC")
fun loadAll(userLoginId: Int): Flow>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt
index 887e43239..89b04ccc8 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MobileDevice.kt
@@ -9,7 +9,7 @@ import java.time.Instant
@Entity(tableName = "MobileDevices")
data class MobileDevice(
- @ColumnInfo(name = "student_id")
+ @ColumnInfo(name = "user_login_id")
val userLoginId: Int,
@ColumnInfo(name = "device_id")
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt
new file mode 100644
index 000000000..d45a81570
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration50.kt
@@ -0,0 +1,21 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration50 : Migration(49, 50) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("DROP TABLE IF EXISTS MobileDevices")
+ database.execSQL(
+ """
+ CREATE TABLE IF NOT EXISTS `MobileDevices` (
+ `user_login_id` INTEGER NOT NULL,
+ `device_id` INTEGER NOT NULL,
+ `name` TEXT NOT NULL,
+ `date` INTEGER NOT NULL,
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)
+ """.trimIndent()
+ )
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt
index b1e96a27b..1a1c501f6 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MobileDeviceMapper.kt
@@ -1,14 +1,14 @@
package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.MobileDevice
-import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.sdk.pojo.Device as SdkDevice
import io.github.wulkanowy.sdk.pojo.Token as SdkToken
-fun List.mapToEntities(semester: Semester) = map {
+fun List.mapToEntities(student: Student) = map {
MobileDevice(
- userLoginId = semester.studentId,
+ userLoginId = student.userLoginId,
date = it.createDateZoned.toInstant(),
deviceId = it.id,
name = it.name
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
index eda40cac4..07c6959e3 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
@@ -39,12 +39,12 @@ class MobileDeviceRepository @Inject constructor(
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
it.isEmpty() || forceRefresh || isExpired
},
- query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) },
+ query = { mobileDb.loadAll(student.userLoginId) },
fetch = {
sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getRegisteredDevices()
- .mapToEntities(semester)
+ .mapToEntities(student)
},
saveFetchResult = { old, new ->
mobileDb.deleteAll(old uniqueSubtract new)
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
index b9a958d43..6865aa7da 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
@@ -56,8 +56,8 @@ class MobileDeviceRepositoryTest {
// prepare
coEvery { sdk.getRegisteredDevices() } returns remoteList
coEvery { mobileDeviceDb.loadAll(student.studentId) } returnsMany listOf(
- flowOf(remoteList.mapToEntities(semester)),
- flowOf(remoteList.mapToEntities(semester))
+ flowOf(remoteList.mapToEntities(student)),
+ flowOf(remoteList.mapToEntities(student))
)
coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { mobileDeviceDb.deleteAll(any()) } just Runs
@@ -79,9 +79,9 @@ class MobileDeviceRepositoryTest {
// prepare
coEvery { sdk.getRegisteredDevices() } returns remoteList
coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf(
- flowOf(remoteList.dropLast(1).mapToEntities(semester)),
- flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
- flowOf(remoteList.mapToEntities(semester))
+ flowOf(remoteList.dropLast(1).mapToEntities(student)),
+ flowOf(remoteList.dropLast(1).mapToEntities(student)), // after fetch end before save result
+ flowOf(remoteList.mapToEntities(student))
)
coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { mobileDeviceDb.deleteAll(any()) } just Runs
@@ -96,7 +96,7 @@ class MobileDeviceRepositoryTest {
coVerify { mobileDeviceDb.loadAll(1) }
coVerify {
mobileDeviceDb.insertAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
+ it.size == 1 && it[0] == remoteList.mapToEntities(student)[1]
})
}
coVerify { mobileDeviceDb.deleteAll(match { it.isEmpty() }) }
@@ -107,9 +107,9 @@ class MobileDeviceRepositoryTest {
// prepare
coEvery { sdk.getRegisteredDevices() } returns remoteList.dropLast(1)
coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf(
- flowOf(remoteList.mapToEntities(semester)),
- flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
- flowOf(remoteList.dropLast(1).mapToEntities(semester))
+ flowOf(remoteList.mapToEntities(student)),
+ flowOf(remoteList.mapToEntities(student)), // after fetch end before save result
+ flowOf(remoteList.dropLast(1).mapToEntities(student))
)
coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { mobileDeviceDb.deleteAll(any()) } just Runs
@@ -125,7 +125,7 @@ class MobileDeviceRepositoryTest {
coVerify { mobileDeviceDb.insertAll(match { it.isEmpty() }) }
coVerify {
mobileDeviceDb.deleteAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
+ it.size == 1 && it[0] == remoteList.mapToEntities(student)[1]
})
}
}
From fcc7dc0913227c595a2575326b80d3ba51ca15d4 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 9 Jul 2022 07:33:27 +0000
Subject: [PATCH 159/669] Bump fragment-ktx from 1.4.1 to 1.5.0 (#1915)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index b7a5c04b0..50c572ed0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -195,7 +195,7 @@ dependencies {
implementation 'androidx.core:core-splashscreen:1.0.0-rc01'
implementation "androidx.activity:activity-ktx:1.4.0"
implementation "androidx.appcompat:appcompat:1.4.2"
- implementation "androidx.fragment:fragment-ktx:1.4.1"
+ implementation "androidx.fragment:fragment-ktx:1.5.0"
implementation "androidx.annotation:annotation:1.4.0"
implementation "androidx.preference:preference-ktx:1.2.0"
From 89a6a98bbf7e15e808b35bea3d8364c5e1cacdca Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 9 Jul 2022 07:33:46 +0000
Subject: [PATCH 160/669] Bump google-services from 4.3.10 to 4.3.13 (#1913)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 7949ceaac..b96c8582e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,7 +15,7 @@ buildscript {
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath 'com.android.tools.build:gradle:7.2.1'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
- classpath 'com.google.gms:google-services:4.3.10'
+ classpath 'com.google.gms:google-services:4.3.13'
classpath 'com.huawei.agconnect:agcp:1.7.0.300'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
From 344e0d55ffb83c0acd75b1d2ed2755a7c9033329 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 9 Jul 2022 07:34:03 +0000
Subject: [PATCH 161/669] Bump lifecycle-livedata-ktx from 2.4.1 to 2.5.0
(#1911)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 50c572ed0..83e18e953 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -212,7 +212,7 @@ dependencies {
implementation "androidx.work:work-runtime-ktx:$work_manager"
playImplementation "androidx.work:work-gcm:$work_manager"
- implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.0"
implementation "androidx.room:room-runtime:$room"
implementation "androidx.room:room-ktx:$room"
From 4b6277abf517162b695788d082126a7e25ba6953 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 9 Jul 2022 07:34:24 +0000
Subject: [PATCH 162/669] Bump activity-ktx from 1.4.0 to 1.5.0 (#1912)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 83e18e953..91c458761 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -193,7 +193,7 @@ dependencies {
implementation "androidx.core:core-ktx:1.8.0"
implementation 'androidx.core:core-splashscreen:1.0.0-rc01'
- implementation "androidx.activity:activity-ktx:1.4.0"
+ implementation "androidx.activity:activity-ktx:1.5.0"
implementation "androidx.appcompat:appcompat:1.4.2"
implementation "androidx.fragment:fragment-ktx:1.5.0"
implementation "androidx.annotation:annotation:1.4.0"
From f1c217b087e46faef8279ab9b0bd47d415c0745a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 12 Jul 2022 10:20:28 +0000
Subject: [PATCH 163/669] Bump kotlin_version from 1.7.0 to 1.7.10 (#1918)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index b96c8582e..71eb2def3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
buildscript {
ext {
- kotlin_version = '1.7.0'
+ kotlin_version = '1.7.10'
about_libraries = '10.3.1'
hilt_version = "2.42"
}
From bdb6c962ea2ac017b3a46c7d7f743b24a6fb7545 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 12 Jul 2022 10:20:42 +0000
Subject: [PATCH 164/669] Bump hianalytics from 6.5.0.300 to 6.6.0.300 (#1919)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 91c458761..588f8dccc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -247,7 +247,7 @@ dependencies {
playImplementation 'com.google.android.play:core-ktx:1.8.1'
playImplementation 'com.google.android.gms:play-services-ads:21.0.0'
- hmsImplementation 'com.huawei.hms:hianalytics:6.5.0.300'
+ hmsImplementation 'com.huawei.hms:hianalytics:6.6.0.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.0.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From 7c4f1c7b22a8a67df9c5575ab74b7651555e4b59 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Tue, 12 Jul 2022 12:23:55 +0200
Subject: [PATCH 165/669] Add auto refresh to reporting units (#1916)
---
.../repositories/ReportingUnitRepository.kt | 27 +++++++++++++------
1 file changed, 19 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt
index b9caf978b..84055cef0 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt
@@ -5,6 +5,8 @@ import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
@@ -13,30 +15,39 @@ import javax.inject.Singleton
@Singleton
class ReportingUnitRepository @Inject constructor(
private val reportingUnitDb: ReportingUnitDao,
- private val sdk: Sdk
+ private val sdk: Sdk,
+ private val refreshHelper: AutoRefreshHelper,
) {
+ private val cacheKey = "reporting_unit"
+
suspend fun refreshReportingUnits(student: Student) {
val new = sdk.init(student).getReportingUnits().mapToEntities(student)
val old = reportingUnitDb.load(student.id.toInt())
reportingUnitDb.deleteAll(old.uniqueSubtract(new))
reportingUnitDb.insertAll(new.uniqueSubtract(old))
+
+ refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
suspend fun getReportingUnits(student: Student): List {
- return reportingUnitDb.load(student.id.toInt()).ifEmpty {
- refreshReportingUnits(student)
+ val cached = reportingUnitDb.load(student.id.toInt())
+ val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
+ return if (cached.isEmpty() || isExpired) {
+ refreshReportingUnits(student)
reportingUnitDb.load(student.id.toInt())
- }
+ } else cached
}
suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? {
- return reportingUnitDb.loadOne(student.id.toInt(), unitId) ?: run {
- refreshReportingUnits(student)
+ val cached = reportingUnitDb.loadOne(student.id.toInt(), unitId)
+ val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
- return reportingUnitDb.loadOne(student.id.toInt(), unitId)
- }
+ return if (cached == null || isExpired) {
+ refreshReportingUnits(student)
+ reportingUnitDb.loadOne(student.id.toInt(), unitId)
+ } else cached
}
}
From fd18583df2ce86969eeab953754c7f7680448d01 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 21 Jul 2022 21:28:20 +0000
Subject: [PATCH 166/669] Bump play-services-ads from 21.0.0 to 21.1.0 (#1922)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 588f8dccc..b51c87f10 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -245,7 +245,7 @@ dependencies {
playImplementation 'com.google.firebase:firebase-crashlytics:'
playImplementation 'com.google.android.play:core:1.10.3'
playImplementation 'com.google.android.play:core-ktx:1.8.1'
- playImplementation 'com.google.android.gms:play-services-ads:21.0.0'
+ playImplementation 'com.google.android.gms:play-services-ads:21.1.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.6.0.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.0.300'
From 08c9539abee8d52ecd3b16738056333ba1aee665 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 21 Jul 2022 21:28:37 +0000
Subject: [PATCH 167/669] Bump coroutines from 1.6.3 to 1.6.4 (#1921)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index b51c87f10..0a848a44c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -180,7 +180,7 @@ ext {
room = "2.4.2"
chucker = "3.5.2"
mockk = "1.12.4"
- coroutines = "1.6.3"
+ coroutines = "1.6.4"
}
dependencies {
From dfc4553fc61540aae4bf84f28b4474f1469ab3c1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 21 Jul 2022 21:28:53 +0000
Subject: [PATCH 168/669] Bump firebase-bom from 30.2.0 to 30.3.0 (#1920)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 0a848a44c..d5df65793 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -239,7 +239,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.7.0'
- playImplementation platform('com.google.firebase:firebase-bom:30.2.0')
+ playImplementation platform('com.google.firebase:firebase-bom:30.3.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From b9be85d99cc11c526431931bf927c52e9a1d446b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 27 Jul 2022 10:09:28 +0000
Subject: [PATCH 169/669] Bump hilt_version from 2.42 to 2.43 (#1923)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 71eb2def3..bb9afab3b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ buildscript {
ext {
kotlin_version = '1.7.10'
about_libraries = '10.3.1'
- hilt_version = "2.42"
+ hilt_version = "2.43"
}
repositories {
mavenCentral()
From efa68f50447f3eb66240cebafb5d156a4e57d780 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 1 Aug 2022 21:16:46 +0000
Subject: [PATCH 170/669] Bump mockk from 1.12.4 to 1.12.5 (#1933)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index d5df65793..2f284d0a3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -179,7 +179,7 @@ ext {
android_hilt = "1.0.0"
room = "2.4.2"
chucker = "3.5.2"
- mockk = "1.12.4"
+ mockk = "1.12.5"
coroutines = "1.6.4"
}
From cf7c6f78eaa7e4f6227b173d30b3bac6b4808539 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 1 Aug 2022 21:17:00 +0000
Subject: [PATCH 171/669] Bump lifecycle-livedata-ktx from 2.5.0 to 2.5.1
(#1932)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 2f284d0a3..2f4f7021c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -212,7 +212,7 @@ dependencies {
implementation "androidx.work:work-runtime-ktx:$work_manager"
playImplementation "androidx.work:work-gcm:$work_manager"
- implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.0"
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
implementation "androidx.room:room-runtime:$room"
implementation "androidx.room:room-ktx:$room"
From d337be0f40c3e1becf59178ff813ff295eef7842 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 1 Aug 2022 21:17:20 +0000
Subject: [PATCH 172/669] Bump firebase-bom from 30.3.0 to 30.3.1 (#1931)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 2f4f7021c..80e3dab46 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -239,7 +239,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.7.0'
- playImplementation platform('com.google.firebase:firebase-bom:30.3.0')
+ playImplementation platform('com.google.firebase:firebase-bom:30.3.1')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From c23a90f1043fc19aa0b71095bb71d9625e4e2c63 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 1 Aug 2022 21:17:35 +0000
Subject: [PATCH 173/669] Bump fragment-ktx from 1.5.0 to 1.5.1 (#1930)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 80e3dab46..ac8df81b2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -195,7 +195,7 @@ dependencies {
implementation 'androidx.core:core-splashscreen:1.0.0-rc01'
implementation "androidx.activity:activity-ktx:1.5.0"
implementation "androidx.appcompat:appcompat:1.4.2"
- implementation "androidx.fragment:fragment-ktx:1.5.0"
+ implementation "androidx.fragment:fragment-ktx:1.5.1"
implementation "androidx.annotation:annotation:1.4.0"
implementation "androidx.preference:preference-ktx:1.2.0"
From cf87339ac40aca6121a863829933cff8f6af935c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 1 Aug 2022 21:18:52 +0000
Subject: [PATCH 174/669] Bump hilt_version from 2.43 to 2.43.1 (#1927)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index bb9afab3b..c88cd35fc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ buildscript {
ext {
kotlin_version = '1.7.10'
about_libraries = '10.3.1'
- hilt_version = "2.43"
+ hilt_version = "2.43.1"
}
repositories {
mavenCentral()
From 378ed0100f7847ee7a25b9479cb9f188a17a080e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 1 Aug 2022 21:26:15 +0000
Subject: [PATCH 175/669] Bump huawei-publish-gradle-plugin from 1.3.3 to 1.3.4
(#1934)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index c88cd35fc..c4dc9c279 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,7 +19,7 @@ buildscript {
classpath 'com.huawei.agconnect:agcp:1.7.0.300'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
- classpath "ru.cian:huawei-publish-gradle-plugin:1.3.3"
+ classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513"
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries"
From 1175740ba22860f27075e12f2c56f4cf6710bc7f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 1 Aug 2022 21:26:34 +0000
Subject: [PATCH 176/669] Bump activity-ktx from 1.5.0 to 1.5.1 (#1926)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index ac8df81b2..4eaa79f16 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -193,7 +193,7 @@ dependencies {
implementation "androidx.core:core-ktx:1.8.0"
implementation 'androidx.core:core-splashscreen:1.0.0-rc01'
- implementation "androidx.activity:activity-ktx:1.5.0"
+ implementation "androidx.activity:activity-ktx:1.5.1"
implementation "androidx.appcompat:appcompat:1.4.2"
implementation "androidx.fragment:fragment-ktx:1.5.1"
implementation "androidx.annotation:annotation:1.4.0"
From b67ecbba4b74bb1336910fe20f0b8ad2754b787f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 1 Aug 2022 21:26:37 +0000
Subject: [PATCH 177/669] Bump room from 2.4.2 to 2.4.3 (#1928)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 4eaa79f16..28aa85848 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -177,7 +177,7 @@ huaweiPublish {
ext {
work_manager = "2.7.1"
android_hilt = "1.0.0"
- room = "2.4.2"
+ room = "2.4.3"
chucker = "3.5.2"
mockk = "1.12.5"
coroutines = "1.6.4"
From dc3a941e24d6cf920a9c52b1a9980e2f76e7230d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 1 Aug 2022 21:36:28 +0000
Subject: [PATCH 178/669] Bump core-splashscreen from 1.0.0-rc01 to 1.0.0
(#1929)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 28aa85848..542dda109 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -192,7 +192,7 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
implementation "androidx.core:core-ktx:1.8.0"
- implementation 'androidx.core:core-splashscreen:1.0.0-rc01'
+ implementation 'androidx.core:core-splashscreen:1.0.0'
implementation "androidx.activity:activity-ktx:1.5.1"
implementation "androidx.appcompat:appcompat:1.4.2"
implementation "androidx.fragment:fragment-ktx:1.5.1"
From b4117aa62eb66497fc43a5ad7574fefc0207229e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 3 Aug 2022 10:56:41 +0000
Subject: [PATCH 179/669] Bump flow-preferences from 1.7.0 to 1.8.0 (#1925)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 542dda109..a4549f08a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -237,7 +237,7 @@ dependencies {
implementation "io.coil-kt:coil:2.1.0"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
- implementation 'com.fredporciuncula:flow-preferences:1.7.0'
+ implementation 'com.fredporciuncula:flow-preferences:1.8.0'
playImplementation platform('com.google.firebase:firebase-bom:30.3.1')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
From 9339e7d9168a695656fb88c5678f008dfee2308e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 10 Aug 2022 09:27:24 +0000
Subject: [PATCH 180/669] Bump gradle from 7.2.1 to 7.2.2 (#1942)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index c4dc9c279..35665b252 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
- classpath 'com.android.tools.build:gradle:7.2.1'
+ classpath 'com.android.tools.build:gradle:7.2.2'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.13'
classpath 'com.huawei.agconnect:agcp:1.7.0.300'
From 96067946d034845495ae7ed074ad9a495cb1c494 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 10 Aug 2022 09:27:46 +0000
Subject: [PATCH 181/669] Bump firebase-bom from 30.3.1 to 30.3.2 (#1940)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index a4549f08a..f8cb8440c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -239,7 +239,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
- playImplementation platform('com.google.firebase:firebase-bom:30.3.1')
+ playImplementation platform('com.google.firebase:firebase-bom:30.3.2')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From ffc0cd840b37b38630ab2b5ca360b5805421ac0e Mon Sep 17 00:00:00 2001
From: Patryk <43276401+Zaptyp@users.noreply.github.com>
Date: Wed, 10 Aug 2022 11:28:22 +0200
Subject: [PATCH 182/669] Update workflow dependency (#1937)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Mikołaj Pich
---
.github/workflows/deploy-store.yml | 20 +++++----
.github/workflows/deploy-test.yml | 20 +++++----
.github/workflows/test.yml | 66 +++++++++++++++++++++++++++---
app/build.gradle | 16 ++++----
build.gradle | 2 +-
5 files changed, 92 insertions(+), 32 deletions(-)
diff --git a/.github/workflows/deploy-store.yml b/.github/workflows/deploy-store.yml
index 12338feff..cfb0fb523 100644
--- a/.github/workflows/deploy-store.yml
+++ b/.github/workflows/deploy-store.yml
@@ -1,4 +1,4 @@
-name: Deploy to app stores
+name: Deploy release
on:
release:
@@ -7,16 +7,17 @@ on:
jobs:
deploy-google-play:
- name: Deploy to google play
+ name: Google Play
runs-on: ubuntu-latest
timeout-minutes: 10
environment: google-play
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-java@v1
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v2
with:
+ distribution: 'zulu'
java-version: 11
- - uses: actions/cache@v2
+ - uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@@ -41,16 +42,17 @@ jobs:
run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace;
deploy-app-gallery:
- name: Deploy to AppGallery
+ name: AppGallery
runs-on: ubuntu-latest
timeout-minutes: 10
environment: app-gallery
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-java@v1
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v2
with:
+ distribution: 'zulu'
java-version: 11
- - uses: actions/cache@v2
+ - uses: actions/cache@v3
with:
path: |
~/.gradle/caches
diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml
index 88edca05d..20082590d 100644
--- a/.github/workflows/deploy-test.yml
+++ b/.github/workflows/deploy-test.yml
@@ -1,4 +1,4 @@
-name: Deploy to app tests
+name: Deploy DEV
on:
push:
@@ -18,11 +18,12 @@ jobs:
timeout-minutes: 10
environment: app-center
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-java@v1
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v2
with:
+ distribution: 'zulu'
java-version: 11
- - uses: actions/cache@v2
+ - uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@@ -66,7 +67,7 @@ jobs:
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
run: ./gradlew assembleFdroidDebug --stacktrace
- name: Upload apk to github artifacts
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: wulkanowyDEV-${{ env.RUN_NUMBER }}.apk
path: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk
@@ -87,11 +88,12 @@ jobs:
environment: app-distribution
if: github.event_name != 'pull_request_target'
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-java@v1
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v2
with:
+ distribution: 'zulu'
java-version: 11
- - uses: actions/cache@v2
+ - uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@@ -131,7 +133,7 @@ jobs:
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
run: ./gradlew assemblePlayDebug -PenableFirebase --stacktrace
- name: Upload apk to github artifacts
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: wulkanowyDEV-${{ env.RUN_NUMBER }}-dev.apk
path: app/build/outputs/apk/play/debug/app-play-debug.apk
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index ee16041f6..3def08953 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -8,18 +8,20 @@ on:
branches: [ master, develop ]
jobs:
- unit-tests:
- name: Unit tests
+
+ tests-fdroid:
+ name: F-Droid
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: fkirc/skip-duplicate-actions@master
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
- - uses: actions/setup-java@v1
+ - uses: actions/setup-java@v2
with:
+ distribution: 'zulu'
java-version: 11
- - uses: actions/cache@v2
+ - uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@@ -29,6 +31,58 @@ jobs:
run: |
./gradlew testFdroidDebugUnitTest --stacktrace
./gradlew jacocoTestReport --stacktrace
- - uses: codecov/codecov-action@v1
+ - uses: codecov/codecov-action@v3
+ with:
+ flags: unit
+
+ tests-play:
+ name: Play
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: fkirc/skip-duplicate-actions@master
+ - uses: actions/checkout@v3
+ - uses: gradle/wrapper-validation-action@v1
+ - uses: actions/setup-java@v2
+ with:
+ distribution: 'zulu'
+ java-version: 11
+ - uses: actions/cache@v3
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
+ - name: Unit tests
+ run: |
+ ./gradlew testPlayDebugUnitTest --stacktrace
+ ./gradlew jacocoTestReport --stacktrace
+ - uses: codecov/codecov-action@v3
+ with:
+ flags: unit
+
+ tests-hms:
+ name: HMS
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: fkirc/skip-duplicate-actions@master
+ - uses: actions/checkout@v3
+ - uses: gradle/wrapper-validation-action@v1
+ - uses: actions/setup-java@v2
+ with:
+ distribution: 'zulu'
+ java-version: 11
+ - uses: actions/cache@v3
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
+ - name: Unit tests
+ run: |
+ ./gradlew testHmsDebugUnitTest --stacktrace
+ ./gradlew jacocoTestReport --stacktrace
+ - uses: codecov/codecov-action@v3
with:
flags: unit
diff --git a/app/build.gradle b/app/build.gradle
index f8cb8440c..78eebff8f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -30,14 +30,14 @@ android {
resValue "string", "app_name", "Wulkanowy"
manifestPlaceholders = [
- firebase_enabled: project.hasProperty("enableFirebase"),
- admob_project_id: ""
+ firebase_enabled: project.hasProperty("enableFirebase"),
+ admob_project_id: ""
]
javaCompileOptions {
annotationProcessorOptions {
arguments += [
- "room.schemaLocation": "$projectDir/schemas".toString(),
- "room.incremental" : "true"
+ "room.schemaLocation": "$projectDir/schemas".toString(),
+ "room.incremental" : "true"
]
}
}
@@ -96,8 +96,8 @@ android {
play {
dimension "platform"
manifestPlaceholders = [
- install_channel : "Google Play",
- admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713"
+ install_channel : "Google Play",
+ admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713"
]
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "\"${System.getenv("SINGLE_SUPPORT_AD_ID") ?: "ca-app-pub-3940256099942544/5354046379"}\""
buildConfigField "String", "DASHBOARD_TILE_AD_ID", "\"${System.getenv("DASHBOARD_TILE_AD_ID") ?: "ca-app-pub-3940256099942544/6300978111"}\""
@@ -126,6 +126,8 @@ android {
testOptions.unitTests {
includeAndroidResources = true
+ // workaround HMS test errors https://github.com/robolectric/robolectric/issues/2750
+ all { jvmArgs '-noverify' }
}
compileOptions {
@@ -184,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:16811fbe90"
+ implementation "io.github.wulkanowy:sdk:9032e33686"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
diff --git a/build.gradle b/build.gradle
index 35665b252..84ed29b95 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ buildscript {
ext {
kotlin_version = '1.7.10'
about_libraries = '10.3.1'
- hilt_version = "2.43.1"
+ hilt_version = "2.43.2"
}
repositories {
mavenCentral()
From c55fd9817991081e24ee9cf1a49fc28eb406c001 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 10 Aug 2022 09:37:53 +0000
Subject: [PATCH 183/669] Bump about_libraries from 10.3.1 to 10.4.0 (#1941)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 84ed29b95..bc4ca5199 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
buildscript {
ext {
kotlin_version = '1.7.10'
- about_libraries = '10.3.1'
+ about_libraries = '10.4.0'
hilt_version = "2.43.2"
}
repositories {
From 5a884a4c56117a04fd21916c2c59c28f70d3439a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 10 Aug 2022 09:38:15 +0000
Subject: [PATCH 184/669] Bump agcp from 1.7.0.300 to 1.7.1.300 (#1938)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index bc4ca5199..98c9dfb84 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.2.2'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.13'
- classpath 'com.huawei.agconnect:agcp:1.7.0.300'
+ classpath 'com.huawei.agconnect:agcp:1.7.1.300'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4"
From 274f9dde07249c79277cc6d5cde1452dfb40fc4f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 10 Aug 2022 09:48:02 +0000
Subject: [PATCH 185/669] Bump agconnect-crash from 1.7.0.300 to 1.7.1.300
(#1943)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 78eebff8f..52b2aad33 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -250,7 +250,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:21.1.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.6.0.300'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.0.300'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.1.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From d64a21b50c2ddee1cce67a5cd02483504df1e1a7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 10 Aug 2022 09:56:20 +0000
Subject: [PATCH 186/669] Bump hianalytics from 6.6.0.300 to 6.7.0.300 (#1944)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 52b2aad33..1f68b65cc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -249,7 +249,7 @@ dependencies {
playImplementation 'com.google.android.play:core-ktx:1.8.1'
playImplementation 'com.google.android.gms:play-services-ads:21.1.0'
- hmsImplementation 'com.huawei.hms:hianalytics:6.6.0.300'
+ hmsImplementation 'com.huawei.hms:hianalytics:6.7.0.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.1.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From 793952cb4479d0838d8dedff5467bf99d60d3ce1 Mon Sep 17 00:00:00 2001
From: Patryk <43276401+Zaptyp@users.noreply.github.com>
Date: Sun, 14 Aug 2022 22:16:47 +0200
Subject: [PATCH 187/669] Fix typo in README DE.md (#1936)
* Update README.de.md
* Change in README-DE.md file
---
README.de.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.de.md b/README.de.md
index 3f806e9fd..b9e1d1ec1 100644
--- a/README.de.md
+++ b/README.de.md
@@ -51,7 +51,7 @@ Die aktuelle Version können Sie von der Google Play, F-Droid oder Huawei AppGal
alt="Explore it on AppGallery"
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
-Sie können auch ein [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) das beinhaltet neue Funktionen, die für die nächste Version vorbereitet werden
+Sie können auch eine [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) die beinhaltet neue Funktionen, die für die nächste Version vorbereitet werden
## Gebaut mit
From 9fe1151a043ba337816269fc0e485d2c356a7d34 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 19 Aug 2022 22:22:47 +0000
Subject: [PATCH 188/669] Bump fragment-ktx from 1.5.1 to 1.5.2 (#1947)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 1f68b65cc..eace462db 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -197,7 +197,7 @@ dependencies {
implementation 'androidx.core:core-splashscreen:1.0.0'
implementation "androidx.activity:activity-ktx:1.5.1"
implementation "androidx.appcompat:appcompat:1.4.2"
- implementation "androidx.fragment:fragment-ktx:1.5.1"
+ implementation "androidx.fragment:fragment-ktx:1.5.2"
implementation "androidx.annotation:annotation:1.4.0"
implementation "androidx.preference:preference-ktx:1.2.0"
From 08a3bd77bde75f066099f276d887b9d44615a7f7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 22 Aug 2022 07:00:10 +0000
Subject: [PATCH 189/669] Bump appcompat from 1.4.2 to 1.5.0 (#1946)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index eace462db..6d33ac476 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -196,7 +196,7 @@ dependencies {
implementation "androidx.core:core-ktx:1.8.0"
implementation 'androidx.core:core-splashscreen:1.0.0'
implementation "androidx.activity:activity-ktx:1.5.1"
- implementation "androidx.appcompat:appcompat:1.4.2"
+ implementation "androidx.appcompat:appcompat:1.5.0"
implementation "androidx.fragment:fragment-ktx:1.5.2"
implementation "androidx.annotation:annotation:1.4.0"
From 9d47127921d5a1d8bff07bb708e284f00ef10bd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Mon, 22 Aug 2022 14:30:50 +0200
Subject: [PATCH 190/669] Add support for messages plus API (#1945)
---
app/build.gradle | 2 +-
.../51.json | 2409 +++++++++++++++++
.../io/github/wulkanowy/data/DataModule.kt | 4 +-
.../github/wulkanowy/data/db/AppDatabase.kt | 9 +-
.../wulkanowy/data/db/dao/MailboxDao.kt | 17 +
.../wulkanowy/data/db/dao/MessagesDao.kt | 8 +-
.../wulkanowy/data/db/dao/RecipientDao.kt | 5 +-
.../wulkanowy/data/db/dao/ReportingUnitDao.kt | 17 -
.../wulkanowy/data/db/entities/Mailbox.kt | 25 +
.../wulkanowy/data/db/entities/Message.kt | 27 +-
.../data/db/entities/MessageAttachment.kt | 7 +-
.../data/db/entities/MessageWithAttachment.kt | 2 +-
.../wulkanowy/data/db/entities/Recipient.kt | 31 +-
.../data/db/entities/ReportingUnit.kt | 32 -
.../data/db/migrations/Migration51.kt | 88 +
.../wulkanowy/data/mappers/MailboxMapper.kt | 18 +
.../wulkanowy/data/mappers/MessageMapper.kt | 46 +-
.../wulkanowy/data/mappers/RecipientMapper.kt | 17 +-
.../data/mappers/ReportingUnitMapper.kt | 16 -
.../data/repositories/MailboxRepository.kt | 48 +
.../data/repositories/MessageRepository.kt | 114 +-
.../data/repositories/RecipientRepository.kt | 37 +-
.../repositories/ReportingUnitRepository.kt | 53 -
.../notifications/NewMessageNotification.kt | 2 +-
.../services/sync/works/MessageWork.kt | 7 +-
.../services/sync/works/RecipientWork.kt | 15 +-
.../modules/dashboard/DashboardPresenter.kt | 4 +-
.../debug/notification/mock/message.kt | 13 +-
.../message/preview/MessagePreviewAdapter.kt | 26 +-
.../message/preview/MessagePreviewFragment.kt | 4 +-
.../preview/MessagePreviewPresenter.kt | 114 +-
.../message/preview/MessagePreviewView.kt | 2 +-
.../message/send/SendMessageActivity.kt | 31 +-
.../message/send/SendMessagePresenter.kt | 128 +-
.../modules/message/send/SendMessageView.kt | 12 +-
.../modules/message/tab/MessageTabAdapter.kt | 11 +-
.../message/tab/MessageTabPresenter.kt | 22 +-
.../main/res/layout/activity_send_message.xml | 3 +-
app/src/main/res/values/strings.xml | 5 +-
.../io/github/wulkanowy/TestEnityCreator.kt | 12 +
.../repositories/MessageRepositoryTest.kt | 116 +-
.../data/repositories/RecipientLocalTest.kt | 81 +-
42 files changed, 3065 insertions(+), 575 deletions(-)
create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json
create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt
delete mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt
delete mode 100644 app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt
delete mode 100644 app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
delete mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt
diff --git a/app/build.gradle b/app/build.gradle
index 6d33ac476..0efb9c303 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:9032e33686"
+ implementation "io.github.wulkanowy:sdk:dbe87aac"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json
new file mode 100644
index 000000000..271b8c90b
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json
@@ -0,0 +1,2409 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 51,
+ "identityHash": "51f9cb1d80df003c03bb655c0162487c",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "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"
+ ],
+ "orders": [],
+ "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}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "kindergartenDiaryId",
+ "columnName": "kindergarten_diary_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "kindergarten_diary_id",
+ "semester_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mailboxKey",
+ "columnName": "mailbox_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondents",
+ "columnName": "correspondents",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "recipients",
+ "columnName": "recipients",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+ "fields": [
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "real_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Mailboxes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `userLoginId` INTEGER NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))",
+ "fields": [
+ {
+ "fieldPath": "globalKey",
+ "columnName": "globalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "userLoginId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "studentName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolNameShort",
+ "columnName": "schoolNameShort",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "globalKey"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "mailboxGlobalKey",
+ "columnName": "mailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentMailboxGlobalKey",
+ "columnName": "studentMailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "schoolShortName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "destination",
+ "columnName": "destination",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'"
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDismissible",
+ "columnName": "is_dismissible",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "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, '51f9cb1d80df003c03bb655c0162487c')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt
index cac3ffc23..22123cbec 100644
--- a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt
@@ -19,7 +19,6 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AppInfo
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
@@ -110,7 +109,6 @@ internal class DataModule {
fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context)
- @OptIn(ExperimentalCoroutinesApi::class)
@Singleton
@Provides
fun provideFlowSharedPref(sharedPreferences: SharedPreferences) =
@@ -197,7 +195,7 @@ internal class DataModule {
@Singleton
@Provides
- fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao
+ fun provideMailboxesDao(database: AppDatabase) = database.mailboxDao
@Singleton
@Provides
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 87915a9ed..15b38805b 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
@@ -30,7 +30,7 @@ import javax.inject.Singleton
Subject::class,
LuckyNumber::class,
CompletedLesson::class,
- ReportingUnit::class,
+ Mailbox::class,
Recipient::class,
MobileDevice::class,
Teacher::class,
@@ -55,7 +55,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
- const val VERSION_SCHEMA = 50
+ const val VERSION_SCHEMA = 51
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
@@ -103,7 +103,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration44(),
Migration46(),
Migration49(),
- Migration50()
+ Migration50(),
+ Migration51(),
)
fun newInstance(
@@ -154,7 +155,7 @@ abstract class AppDatabase : RoomDatabase() {
abstract val completedLessonsDao: CompletedLessonsDao
- abstract val reportingUnitDao: ReportingUnitDao
+ abstract val mailboxDao: MailboxDao
abstract val recipientDao: RecipientDao
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt
new file mode 100644
index 000000000..8589db311
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt
@@ -0,0 +1,17 @@
+package io.github.wulkanowy.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import io.github.wulkanowy.data.db.entities.Mailbox
+import javax.inject.Singleton
+
+@Singleton
+@Dao
+interface MailboxDao : BaseDao {
+
+ @Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId ")
+ suspend fun loadAll(userLoginId: Int): List
+
+ @Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId AND studentName = :studentName ")
+ suspend fun load(userLoginId: Int, studentName: String): Mailbox?
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt
index 729ba6a68..8c730c9bc 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt
@@ -11,9 +11,9 @@ import kotlinx.coroutines.flow.Flow
interface MessagesDao : BaseDao {
@Transaction
- @Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId")
- fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow
+ @Query("SELECT * FROM Messages WHERE message_global_key = :messageGlobalKey")
+ fun loadMessageWithAttachment(messageGlobalKey: String): Flow
- @Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC")
- fun loadAll(studentId: Int, folder: Int): Flow>
+ @Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
+ fun loadAll(mailboxKey: String, folder: Int): Flow>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt
index c2787ac3b..1956261eb 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/RecipientDao.kt
@@ -2,6 +2,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
+import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Recipient
import javax.inject.Singleton
@@ -9,6 +10,6 @@ import javax.inject.Singleton
@Dao
interface RecipientDao : BaseDao {
- @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND unit_id = :unitId AND role = :role")
- suspend fun loadAll(studentId: Int, unitId: Int, role: Int): List
+ @Query("SELECT * FROM Recipients WHERE type = :type AND studentMailboxGlobalKey = :studentMailboxGlobalKey")
+ suspend fun loadAll(type: MailboxType, studentMailboxGlobalKey: String): List
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt
deleted file mode 100644
index ca697eda8..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/ReportingUnitDao.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package io.github.wulkanowy.data.db.dao
-
-import androidx.room.Dao
-import androidx.room.Query
-import io.github.wulkanowy.data.db.entities.ReportingUnit
-import javax.inject.Singleton
-
-@Singleton
-@Dao
-interface ReportingUnitDao : BaseDao {
-
- @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId")
- suspend fun load(studentId: Int): List
-
- @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId")
- suspend fun loadOne(studentId: Int, unitId: Int): ReportingUnit?
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt
new file mode 100644
index 000000000..7c08e481d
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt
@@ -0,0 +1,25 @@
+package io.github.wulkanowy.data.db.entities
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "Mailboxes")
+data class Mailbox(
+
+ @PrimaryKey
+ val globalKey: String,
+ val fullName: String,
+ val userName: String,
+ val userLoginId: Int,
+ val studentName: String,
+ val schoolNameShort: String,
+ val type: MailboxType,
+)
+
+enum class MailboxType {
+ STUDENT,
+ PARENT,
+ GUARDIAN,
+ EMPLOYEE,
+ UNKNOWN,
+}
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 8782bc765..77874e03d 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
@@ -9,23 +9,16 @@ import java.time.Instant
@Entity(tableName = "Messages")
data class Message(
- @ColumnInfo(name = "student_id")
- val studentId: Long,
+ @ColumnInfo(name = "message_global_key")
+ val messageGlobalKey: String,
- @ColumnInfo(name = "real_id")
- val realId: Int,
+ @ColumnInfo(name = "mailbox_key")
+ val mailboxKey: String,
@ColumnInfo(name = "message_id")
val messageId: Int,
- @ColumnInfo(name = "sender_name")
- val sender: String,
-
- @ColumnInfo(name = "sender_id")
- val senderId: Int,
-
- @ColumnInfo(name = "recipient_name")
- val recipient: String,
+ val correspondents: String,
val subject: String,
@@ -36,8 +29,6 @@ data class Message(
var unread: Boolean,
- val removed: Boolean,
-
@ColumnInfo(name = "has_attachments")
val hasAttachments: Boolean
) : Serializable {
@@ -48,11 +39,7 @@ data class Message(
@ColumnInfo(name = "is_notified")
var isNotified: Boolean = true
- @ColumnInfo(name = "unread_by")
- var unreadBy: Int = 0
-
- @ColumnInfo(name = "read_by")
- var readBy: Int = 0
-
var content: String = ""
+ var sender: String? = null
+ var recipients: String? = null
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt
index d1886e910..93f042999 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageAttachment.kt
@@ -12,11 +12,8 @@ data class MessageAttachment(
@ColumnInfo(name = "real_id")
val realId: Int,
- @ColumnInfo(name = "message_id")
- val messageId: Int,
-
- @ColumnInfo(name = "one_drive_id")
- val oneDriveId: String,
+ @ColumnInfo(name = "message_global_key")
+ val messageGlobalKey: String,
@ColumnInfo(name = "url")
val url: String,
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt
index 2e7af0f40..cd468215d 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt
@@ -7,6 +7,6 @@ data class MessageWithAttachment(
@Embedded
val message: Message,
- @Relation(parentColumn = "message_id", entityColumn = "message_id")
+ @Relation(parentColumn = "message_global_key", entityColumn = "message_global_key")
val attachments: List
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt
index 223322705..d09742cd2 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Recipient.kt
@@ -1,6 +1,5 @@
package io.github.wulkanowy.data.db.entities
-import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
@@ -8,32 +7,16 @@ import java.io.Serializable
@kotlinx.serialization.Serializable
@Entity(tableName = "Recipients")
data class Recipient(
-
- @ColumnInfo(name = "student_id")
- val studentId: Int,
-
- @ColumnInfo(name = "real_id")
- val realId: String,
-
- val name: String,
-
- @ColumnInfo(name = "real_name")
- val realName: String,
-
- @ColumnInfo(name = "login_id")
- val loginId: Int,
-
- @ColumnInfo(name = "unit_id")
- val unitId: Int,
-
- val role: Int,
-
- val hash: String
-
+ val mailboxGlobalKey: String,
+ val studentMailboxGlobalKey: String,
+ val fullName: String,
+ val userName: String,
+ val schoolShortName: String,
+ val type: MailboxType,
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
- override fun toString() = name
+ override fun toString() = userName
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt
deleted file mode 100644
index 0570a2ffd..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/ReportingUnit.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package io.github.wulkanowy.data.db.entities
-
-import androidx.room.ColumnInfo
-import androidx.room.Entity
-import androidx.room.PrimaryKey
-import java.io.Serializable
-
-@Entity(tableName = "ReportingUnits")
-data class ReportingUnit(
-
- @ColumnInfo(name = "student_id")
- val studentId: Int,
-
- @ColumnInfo(name = "real_id")
- val unitId: Int,
-
- @ColumnInfo(name = "short")
- val shortName: String,
-
- @ColumnInfo(name = "sender_id")
- val senderId: Int,
-
- @ColumnInfo(name = "sender_name")
- val senderName: String,
-
- val roles: List
-
-) : Serializable {
-
- @PrimaryKey(autoGenerate = true)
- var id: Long = 0
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt
new file mode 100644
index 000000000..e78e2e3a7
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration51.kt
@@ -0,0 +1,88 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration51 : Migration(50, 51) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ createMailboxTable(database)
+ recreateMessagesTable(database)
+ recreateMessageAttachmentsTable(database)
+ recreateRecipientsTable(database)
+ deleteReportingUnitTable(database)
+ }
+
+ private fun createMailboxTable(database: SupportSQLiteDatabase) {
+ database.execSQL("DROP TABLE IF EXISTS Mailboxes")
+ database.execSQL(
+ """
+ CREATE TABLE IF NOT EXISTS `Mailboxes` (
+ `globalKey` TEXT NOT NULL,
+ `fullName` TEXT NOT NULL,
+ `userName` TEXT NOT NULL,
+ `userLoginId` INTEGER NOT NULL,
+ `studentName` TEXT NOT NULL,
+ `schoolNameShort` TEXT NOT NULL,
+ `type` TEXT NOT NULL,
+ PRIMARY KEY(`globalKey`)
+ )""".trimIndent()
+ )
+ }
+
+ private fun recreateMessagesTable(database: SupportSQLiteDatabase) {
+ database.execSQL("DROP TABLE IF EXISTS Messages")
+ database.execSQL(
+ """
+ CREATE TABLE IF NOT EXISTS `Messages` (
+ `message_global_key` TEXT NOT NULL,
+ `mailbox_key` TEXT NOT NULL,
+ `message_id` INTEGER NOT NULL,
+ `correspondents` TEXT NOT NULL,
+ `subject` TEXT NOT NULL,
+ `date` INTEGER NOT NULL,
+ `folder_id` INTEGER NOT NULL,
+ `unread` INTEGER NOT NULL,
+ `has_attachments` INTEGER NOT NULL,
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ `is_notified` INTEGER NOT NULL,
+ `content` TEXT NOT NULL,
+ `sender` TEXT, `recipients` TEXT
+ )""".trimIndent()
+ )
+ }
+
+ private fun recreateMessageAttachmentsTable(database: SupportSQLiteDatabase) {
+ database.execSQL("DROP TABLE IF EXISTS MessageAttachments")
+ database.execSQL(
+ """
+ CREATE TABLE IF NOT EXISTS `MessageAttachments` (
+ `real_id` INTEGER NOT NULL,
+ `message_global_key` TEXT NOT NULL,
+ `url` TEXT NOT NULL,
+ `filename` TEXT NOT NULL,
+ PRIMARY KEY(`real_id`)
+ )""".trimIndent()
+ )
+ }
+
+ private fun recreateRecipientsTable(database: SupportSQLiteDatabase) {
+ database.execSQL("DROP TABLE IF EXISTS Recipients")
+ database.execSQL(
+ """
+ CREATE TABLE IF NOT EXISTS `Recipients` (
+ `mailboxGlobalKey` TEXT NOT NULL,
+ `studentMailboxGlobalKey` TEXT NOT NULL,
+ `fullName` TEXT NOT NULL,
+ `userName` TEXT NOT NULL,
+ `schoolShortName` TEXT NOT NULL,
+ `type` TEXT NOT NULL,
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
+ )""".trimIndent()
+ )
+ }
+
+ private fun deleteReportingUnitTable(database: SupportSQLiteDatabase) {
+ database.execSQL("DROP TABLE IF EXISTS ReportingUnits")
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt
new file mode 100644
index 000000000..2ccca1b90
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt
@@ -0,0 +1,18 @@
+package io.github.wulkanowy.data.mappers
+
+import io.github.wulkanowy.data.db.entities.Mailbox
+import io.github.wulkanowy.data.db.entities.MailboxType
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.sdk.pojo.Mailbox as SdkMailbox
+
+fun List.mapToEntities(student: Student) = map {
+ Mailbox(
+ globalKey = it.globalKey,
+ fullName = it.fullName,
+ userName = it.userName,
+ userLoginId = student.userLoginId,
+ studentName = it.studentName,
+ schoolNameShort = it.schoolNameShort,
+ type = MailboxType.valueOf(it.type.name),
+ )
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
index 13f0ab33e..2e7967f0e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
@@ -1,40 +1,31 @@
package io.github.wulkanowy.data.mappers
-import io.github.wulkanowy.data.db.entities.Message
-import io.github.wulkanowy.data.db.entities.MessageAttachment
-import io.github.wulkanowy.data.db.entities.Recipient
-import io.github.wulkanowy.data.db.entities.Student
-import java.time.Instant
+import io.github.wulkanowy.data.db.entities.*
+import io.github.wulkanowy.sdk.pojo.MailboxType
import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
-fun List.mapToEntities(student: Student) = map {
+fun List.mapToEntities(mailbox: Mailbox) = map {
Message(
- studentId = student.id,
- realId = it.id ?: 0,
- messageId = it.messageId ?: 0,
- sender = it.sender?.name.orEmpty(),
- senderId = it.sender?.loginId ?: 0,
- recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów",
+ messageGlobalKey = it.globalKey,
+ mailboxKey = mailbox.globalKey,
+ messageId = it.id,
+ correspondents = it.correspondents,
subject = it.subject.trim(),
- date = it.dateZoned?.toInstant() ?: Instant.now(),
+ date = it.dateZoned.toInstant(),
folderId = it.folderId,
- unread = it.unread ?: false,
- removed = it.removed,
+ unread = it.unread,
hasAttachments = it.hasAttachments
).apply {
content = it.content.orEmpty()
- unreadBy = it.unreadBy ?: 0
- readBy = it.readBy ?: 0
}
}
-fun List.mapToEntities() = map {
+fun List.mapToEntities(messageGlobalKey: String) = map {
MessageAttachment(
- realId = it.id,
- messageId = it.messageId,
- oneDriveId = it.oneDriveId,
+ messageGlobalKey = messageGlobalKey,
+ realId = it.url.hashCode(),
url = it.url,
filename = it.filename
)
@@ -42,12 +33,11 @@ fun List.mapToEntities() = map {
fun List.mapFromEntities() = map {
SdkRecipient(
- id = it.realId,
- name = it.realName,
- loginId = it.loginId,
- reportingUnitId = it.unitId,
- role = it.role,
- hash = it.hash,
- shortName = it.name
+ fullName = it.fullName,
+ userName = it.userName,
+ studentName = it.userName,
+ mailboxGlobalKey = it.mailboxGlobalKey,
+ schoolNameShort = it.schoolShortName,
+ type = MailboxType.valueOf(it.type.name),
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt
index 80bddaab1..eb993a0f0 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/RecipientMapper.kt
@@ -1,17 +1,16 @@
package io.github.wulkanowy.data.mappers
+import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
-fun List.mapToEntities(userLoginId: Int) = map {
+fun List.mapToEntities(studentMailboxGlobalKey: String) = map {
Recipient(
- studentId = userLoginId,
- realId = it.id,
- realName = it.name,
- name = it.shortName,
- hash = it.hash,
- loginId = it.loginId,
- role = it.role,
- unitId = it.reportingUnitId ?: 0
+ mailboxGlobalKey = it.mailboxGlobalKey,
+ fullName = it.fullName,
+ userName = it.userName,
+ studentMailboxGlobalKey = studentMailboxGlobalKey,
+ schoolShortName = it.schoolNameShort,
+ type = MailboxType.valueOf(it.type.name),
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt
deleted file mode 100644
index 6a21d59fc..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/ReportingUnitMapper.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.github.wulkanowy.data.mappers
-
-import io.github.wulkanowy.data.db.entities.ReportingUnit
-import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.sdk.pojo.ReportingUnit as SdkReportingUnit
-
-fun List.mapToEntities(student: Student) = map {
- ReportingUnit(
- studentId = student.id.toInt(),
- unitId = it.id,
- roles = it.roles,
- senderId = it.senderId,
- senderName = it.senderName,
- shortName = it.short
- )
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
new file mode 100644
index 000000000..7f5974920
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
@@ -0,0 +1,48 @@
+package io.github.wulkanowy.data.repositories
+
+import io.github.wulkanowy.data.db.dao.MailboxDao
+import io.github.wulkanowy.data.db.entities.Mailbox
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.sdk.Sdk
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.uniqueSubtract
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class MailboxRepository @Inject constructor(
+ private val mailboxDao: MailboxDao,
+ private val sdk: Sdk,
+ private val refreshHelper: AutoRefreshHelper,
+) {
+ private val cacheKey = "mailboxes"
+
+ suspend fun refreshMailboxes(student: Student) {
+ val new = sdk.init(student).getMailboxes().mapToEntities(student)
+ val old = mailboxDao.loadAll(student.userLoginId)
+
+ mailboxDao.deleteAll(old uniqueSubtract new)
+ mailboxDao.insertAll(new uniqueSubtract old)
+
+ refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
+ }
+
+ suspend fun getMailbox(student: Student): Mailbox {
+ val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
+ val mailbox = mailboxDao.load(student.userLoginId, student.studentName)
+
+ return if (isExpired || mailbox == null) {
+ refreshMailboxes(student)
+ val newMailbox = mailboxDao.load(student.userLoginId, student.studentName)
+
+ requireNotNull(newMailbox) {
+ "Mailbox for ${student.userName} - ${student.studentName} not found!"
+ }
+
+ newMailbox
+ } else mailbox
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
index 05fb97657..00cbffb84 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
@@ -10,24 +10,24 @@ import io.github.wulkanowy.data.db.dao.MessagesDao
import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
+import io.github.wulkanowy.data.enums.MessageFolder.TRASHED
import io.github.wulkanowy.data.mappers.mapFromEntities
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.data.pojos.MessageDraft
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Folder
-import io.github.wulkanowy.sdk.pojo.SentMessage
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import timber.log.Timber
-import java.time.LocalDateTime.now
import javax.inject.Inject
import javax.inject.Singleton
@@ -49,7 +49,7 @@ class MessageRepository @Inject constructor(
@Suppress("UNUSED_PARAMETER")
fun getMessages(
student: Student,
- semester: Semester,
+ mailbox: Mailbox,
folder: MessageFolder,
forceRefresh: Boolean,
notify: Boolean = false,
@@ -62,42 +62,20 @@ class MessageRepository @Inject constructor(
)
it.isEmpty() || forceRefresh || isExpired
},
- query = { messagesDb.loadAll(student.id.toInt(), folder.id) },
+ query = { messagesDb.loadAll(mailbox.globalKey, folder.id) },
fetch = {
- sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now())
- .mapToEntities(student)
+ sdk.init(student).getMessages(Folder.valueOf(folder.name)).mapToEntities(mailbox)
},
saveFetchResult = { old, new ->
messagesDb.deleteAll(old uniqueSubtract new)
messagesDb.insertAll((new uniqueSubtract old).onEach {
it.isNotified = !notify
})
- messagesDb.updateAll(getMessagesWithReadByChange(old, new, !notify))
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder))
}
)
- private fun getMessagesWithReadByChange(
- old: List,
- new: List,
- setNotified: Boolean
- ): List {
- val oldMeta = old.map { Triple(it, it.readBy, it.unreadBy) }
- val newMeta = new.map { Triple(it, it.readBy, it.unreadBy) }
-
- val updatedItems = newMeta uniqueSubtract oldMeta
-
- return updatedItems.map {
- val oldItem = old.find { item -> item.messageId == it.first.messageId }
- it.first.apply {
- id = oldItem?.id ?: 0
- isNotified = oldItem?.isNotified ?: setNotified
- content = oldItem?.content.orEmpty()
- }
- }
- }
-
fun getMessage(
student: Student,
message: Message,
@@ -106,34 +84,34 @@ class MessageRepository @Inject constructor(
isResultEmpty = { it?.message?.content.isNullOrBlank() },
shouldFetch = {
checkNotNull(it) { "This message no longer exist!" }
- Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
- it.message.unread || it.message.content.isEmpty()
+ Timber.d("Message content in db empty: ${it.message.content.isBlank()}")
+ it.message.unread || it.message.content.isBlank()
},
- query = { messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) },
+ query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) },
fetch = {
- sdk.init(student).getMessageDetails(
- messageId = it!!.message.messageId,
- folderId = message.folderId,
- read = markAsRead,
- id = message.realId
- ).let { details ->
- details.content to details.attachments.mapToEntities()
- }
+ sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey)
},
- saveFetchResult = { old, (downloadedMessage, attachments) ->
+ saveFetchResult = { old, new ->
checkNotNull(old) { "Fetched message no longer exist!" }
- messagesDb.updateAll(listOf(old.message.apply {
- id = old.message.id
- unread = !markAsRead
- content = content.ifBlank { downloadedMessage }
- }))
- messageAttachmentDao.insertAttachments(attachments)
+ messagesDb.updateAll(
+ listOf(old.message.apply {
+ id = message.id
+ unread = !markAsRead
+ sender = new.sender
+ recipients = new.recipients.firstOrNull() ?: "Wielu adresoatów"
+ content = content.ifBlank { new.content }
+ })
+ )
+ messageAttachmentDao.insertAttachments(
+ items = new.attachments.mapToEntities(message.messageGlobalKey),
+ )
+
Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read")
}
)
- fun getMessagesFromDatabase(student: Student): Flow> {
- return messagesDb.loadAll(student.id.toInt(), RECEIVED.id)
+ fun getMessagesFromDatabase(mailbox: Mailbox): Flow> {
+ return messagesDb.loadAll(mailbox.globalKey, RECEIVED.id)
}
suspend fun updateMessages(messages: List) {
@@ -145,32 +123,48 @@ class MessageRepository @Inject constructor(
subject: String,
content: String,
recipients: List,
- ): SentMessage = sdk.init(student).sendMessage(
- subject = subject,
- content = content,
- recipients = recipients.mapFromEntities()
- )
+ mailboxId: String,
+ ) {
+ sdk.init(student).sendMessage(
+ subject = subject,
+ content = content,
+ recipients = recipients.mapFromEntities(),
+ mailboxId = mailboxId,
+ )
+ }
- suspend fun deleteMessages(student: Student, messages: List) {
- val folderId = messages.first().folderId
- val isDeleted = sdk.init(student)
- .deleteMessages(messages = messages.map { it.messageId }, folderId = folderId)
+ suspend fun deleteMessages(student: Student, mailbox: Mailbox, messages: List) {
+ val firstMessage = messages.first()
+ sdk.init(student).deleteMessages(
+ messages = messages.map { it.messageGlobalKey },
+ removeForever = firstMessage.folderId == TRASHED.id,
+ )
- if (folderId != MessageFolder.TRASHED.id && isDeleted) {
+ if (firstMessage.folderId != TRASHED.id) {
val deletedMessages = messages.map {
- it.copy(folderId = MessageFolder.TRASHED.id)
+ it.copy(folderId = TRASHED.id)
.apply {
id = it.id
content = it.content
+ sender = it.sender
+ recipients = it.recipients
}
}
messagesDb.updateAll(deletedMessages)
} else messagesDb.deleteAll(messages)
+
+ getMessages(
+ student = student,
+ mailbox = mailbox,
+ folder = TRASHED,
+ forceRefresh = true,
+ ).first()
}
- suspend fun deleteMessage(student: Student, message: Message) =
- deleteMessages(student, listOf(message))
+ suspend fun deleteMessage(student: Student, mailbox: Mailbox, message: Message) {
+ deleteMessages(student, mailbox, listOf(message))
+ }
var draftMessage: MessageDraft?
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft))
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt
index 60e6f248f..e80f028e1 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt
@@ -1,10 +1,7 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.RecipientDao
-import io.github.wulkanowy.data.db.entities.Message
-import io.github.wulkanowy.data.db.entities.Recipient
-import io.github.wulkanowy.data.db.entities.ReportingUnit
-import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
@@ -23,9 +20,10 @@ class RecipientRepository @Inject constructor(
private val cacheKey = "recipient"
- suspend fun refreshRecipients(student: Student, unit: ReportingUnit, role: Int) {
- val new = sdk.init(student).getRecipients(unit.unitId, role).mapToEntities(unit.studentId)
- val old = recipientDb.loadAll(unit.studentId, unit.unitId, role)
+ suspend fun refreshRecipients(student: Student, mailbox: Mailbox, type: MailboxType) {
+ val new = sdk.init(student).getRecipients(mailbox.globalKey)
+ .mapToEntities(mailbox.globalKey)
+ val old = recipientDb.loadAll(type, mailbox.globalKey)
recipientDb.deleteAll(old uniqueSubtract new)
recipientDb.insertAll(new uniqueSubtract old)
@@ -33,18 +31,27 @@ class RecipientRepository @Inject constructor(
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
- suspend fun getRecipients(student: Student, unit: ReportingUnit, role: Int): List {
- val cached = recipientDb.loadAll(unit.studentId, unit.unitId, role)
+ suspend fun getRecipients(
+ student: Student,
+ mailbox: Mailbox,
+ type: MailboxType
+ ): List {
+ val cached = recipientDb.loadAll(type, mailbox.globalKey)
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
return if (cached.isEmpty() || isExpired) {
- refreshRecipients(student, unit, role)
- recipientDb.loadAll(unit.studentId, unit.unitId, role)
+ refreshRecipients(student, mailbox, type)
+ recipientDb.loadAll(type, mailbox.globalKey)
} else cached
}
- suspend fun getMessageRecipients(student: Student, message: Message): List {
- return sdk.init(student).getMessageRecipients(message.messageId, message.senderId)
- .mapToEntities(student.studentId)
- }
+ suspend fun getMessageSender(
+ student: Student,
+ mailbox: Mailbox,
+ message: Message
+ ): List = sdk.init(student)
+ .getMessageReplayDetails(message.messageGlobalKey)
+ .sender
+ .let(::listOf)
+ .mapToEntities(mailbox.globalKey)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt
deleted file mode 100644
index 84055cef0..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ReportingUnitRepository.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-package io.github.wulkanowy.data.repositories
-
-import io.github.wulkanowy.data.db.dao.ReportingUnitDao
-import io.github.wulkanowy.data.db.entities.ReportingUnit
-import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.mappers.mapToEntities
-import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.uniqueSubtract
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class ReportingUnitRepository @Inject constructor(
- private val reportingUnitDb: ReportingUnitDao,
- private val sdk: Sdk,
- private val refreshHelper: AutoRefreshHelper,
-) {
-
- private val cacheKey = "reporting_unit"
-
- suspend fun refreshReportingUnits(student: Student) {
- val new = sdk.init(student).getReportingUnits().mapToEntities(student)
- val old = reportingUnitDb.load(student.id.toInt())
-
- reportingUnitDb.deleteAll(old.uniqueSubtract(new))
- reportingUnitDb.insertAll(new.uniqueSubtract(old))
-
- refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
- }
-
- suspend fun getReportingUnits(student: Student): List {
- val cached = reportingUnitDb.load(student.id.toInt())
- val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
-
- return if (cached.isEmpty() || isExpired) {
- refreshReportingUnits(student)
- reportingUnitDb.load(student.id.toInt())
- } else cached
- }
-
- suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? {
- val cached = reportingUnitDb.loadOne(student.id.toInt(), unitId)
- val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
-
- return if (cached == null || isExpired) {
- refreshReportingUnits(student)
- reportingUnitDb.loadOne(student.id.toInt(), unitId)
- } else cached
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt
index 5c3c52c5b..3b7bcff05 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt
@@ -21,7 +21,7 @@ class NewMessageNotification @Inject constructor(
val notificationDataList = items.map {
NotificationData(
title = context.getPlural(R.plurals.message_new_items, 1),
- content = "${it.sender}: ${it.subject}",
+ content = "${it.correspondents}: ${it.subject}",
destination = Destination.Message,
)
}
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 26fac1a2f..180568267 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
@@ -3,6 +3,7 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
+import io.github.wulkanowy.data.repositories.MailboxRepository
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewMessageNotification
@@ -11,19 +12,21 @@ import javax.inject.Inject
class MessageWork @Inject constructor(
private val messageRepository: MessageRepository,
+ private val mailboxRepository: MailboxRepository,
private val newMessageNotification: NewMessageNotification,
) : Work {
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
+ val mailbox = mailboxRepository.getMailbox(student)
messageRepository.getMessages(
student = student,
- semester = semester,
+ mailbox = mailbox,
folder = RECEIVED,
forceRefresh = true,
notify = notify
).waitForResult()
- messageRepository.getMessagesFromDatabase(student).first()
+ messageRepository.getMessagesFromDatabase(mailbox).first()
.filter { !it.isNotified && it.unread }.let {
if (it.isNotEmpty()) newMessageNotification.notify(it, student)
messageRepository.updateMessages(it.onEach { message -> message.isNotified = true })
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt
index 425e68b91..b1322ada3 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt
@@ -1,23 +1,22 @@
package io.github.wulkanowy.services.sync.works
+import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.repositories.MailboxRepository
import io.github.wulkanowy.data.repositories.RecipientRepository
-import io.github.wulkanowy.data.repositories.ReportingUnitRepository
import javax.inject.Inject
class RecipientWork @Inject constructor(
- private val reportingUnitRepository: ReportingUnitRepository,
+ private val mailboxRepository: MailboxRepository,
private val recipientRepository: RecipientRepository
) : Work {
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
- reportingUnitRepository.refreshReportingUnits(student)
+ mailboxRepository.refreshMailboxes(student)
- reportingUnitRepository.getReportingUnits(student).let { units ->
- units.map {
- recipientRepository.refreshRecipients(student, it, 2)
- }
- }
+ val mailbox = mailboxRepository.getMailbox(student)
+
+ recipientRepository.refreshRecipients(student, mailbox, MailboxType.EMPLOYEE)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index 5d7c7df4b..350300937 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -25,6 +25,7 @@ class DashboardPresenter @Inject constructor(
private val gradeRepository: GradeRepository,
private val semesterRepository: SemesterRepository,
private val messageRepository: MessageRepository,
+ private val mailboxRepository: MailboxRepository,
private val attendanceSummaryRepository: AttendanceSummaryRepository,
private val timetableRepository: TimetableRepository,
private val homeworkRepository: HomeworkRepository,
@@ -227,6 +228,7 @@ class DashboardPresenter @Inject constructor(
private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) {
flow {
val semester = semesterRepository.getCurrentSemester(student)
+ val mailbox = mailboxRepository.getMailbox(student)
val selectedTiles = preferencesRepository.selectedDashboardTiles
val flowSuccess = flowOf(Resource.Success(null))
@@ -238,7 +240,7 @@ class DashboardPresenter @Inject constructor(
val messageFLow = messageRepository.getMessages(
student = student,
- semester = semester,
+ mailbox = mailbox,
folder = MessageFolder.RECEIVED,
forceRefresh = forceRefresh
).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt
index 53d439612..6ff26162b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt
@@ -17,16 +17,13 @@ val debugMessageItems = listOf(
)
private fun generateMessage(sender: String, subject: String) = Message(
- sender = sender,
subject = subject,
- studentId = 0,
- realId = 0,
- messageId = 0,
- senderId = 0,
- recipient = "",
+ messageId = 123,
date = Instant.now(),
folderId = 0,
unread = true,
- removed = false,
- hasAttachments = false
+ hasAttachments = false,
+ messageGlobalKey = "",
+ correspondents = sender,
+ mailboxKey = "",
)
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 d75128be1..3c1c53d39 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
@@ -4,6 +4,8 @@ import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT
+import androidx.core.text.parseAsHtml
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
@@ -75,29 +77,25 @@ class MessagePreviewAdapter @Inject constructor() :
@SuppressLint("SetTextI18n")
private fun bindMessage(holder: MessageViewHolder, message: Message) {
val context = holder.binding.root.context
- val recipientCount = message.unreadBy + message.readBy
- val readText = when {
- recipientCount > 1 -> {
- context.getString(R.string.message_read_by, message.readBy, recipientCount)
- }
- message.readBy == 1 -> {
- context.getString(R.string.message_read, context.getString(R.string.all_yes))
- }
- else -> context.getString(R.string.message_read, context.getString(R.string.all_no))
+ val readTextValue = when {
+ !message.unread -> R.string.all_yes
+ else -> R.string.all_no
}
+ val readText = context.getString(R.string.message_read, context.getString(readTextValue))
with(holder.binding) {
- messagePreviewSubject.text =
- message.subject.ifBlank { root.context.getString(R.string.message_no_subject) }
- messagePreviewDate.text = root.context.getString(
+ messagePreviewSubject.text = message.subject.ifBlank {
+ context.getString(R.string.message_no_subject)
+ }
+ messagePreviewDate.text = context.getString(
R.string.message_date,
message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
)
messagePreviewRead.text = readText
- messagePreviewContent.text = message.content
+ messagePreviewContent.text = message.content.parseAsHtml(FROM_HTML_MODE_COMPACT)
messagePreviewFromSender.text = message.sender
- messagePreviewToRecipient.text = message.recipient
+ messagePreviewToRecipient.text = message.recipients
}
}
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 4b2685c6d..2a5523f4d 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
@@ -135,8 +135,8 @@ class MessagePreviewFragment :
binding.messagePreviewRecycler.visibility = if (show) VISIBLE else GONE
}
- override fun showOptions(show: Boolean) {
- menuReplyButton?.isVisible = show
+ override fun showOptions(show: Boolean, isReplayable: Boolean) {
+ menuReplyButton?.isVisible = isReplayable
menuForwardButton?.isVisible = show
menuDeleteButton?.isVisible = show
menuShareButton?.isVisible = show
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 39c337bf2..c011f41f3 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,10 +1,12 @@
package io.github.wulkanowy.ui.modules.message.preview
import android.annotation.SuppressLint
+import androidx.core.text.parseAsHtml
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.enums.MessageFolder
+import io.github.wulkanowy.data.repositories.MailboxRepository
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
@@ -19,6 +21,7 @@ class MessagePreviewPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val messageRepository: MessageRepository,
+ private val mailboxRepository: MailboxRepository,
private val analytics: AnalyticsHelper
) : BasePresenter(errorHandler, studentRepository) {
@@ -52,7 +55,7 @@ class MessagePreviewPresenter @Inject constructor(
private fun loadData(messageToLoad: Message) {
flatResourceFlow {
- val student = studentRepository.getStudentById(messageToLoad.studentId)
+ val student = studentRepository.getCurrentStudent()
messageRepository.getMessage(student, messageToLoad, true)
}
.logResourceStatus("message ${messageToLoad.messageId} preview")
@@ -104,62 +107,69 @@ class MessagePreviewPresenter @Inject constructor(
}
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}"
+ val message = message ?: return false
+ val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
- attachments?.let { attachments ->
- if (attachments.isNotEmpty()) {
- text += "\n\nZałączniki:"
+ val text = buildString {
+ appendLine("Temat: $subject")
+ appendLine("Od: ${message.sender}")
+ appendLine("Do: ${message.recipients}")
+ appendLine("Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}")
- attachments.forEach { attachment ->
- text += "\n${attachment.filename}: ${attachment.url}"
- }
- }
+ appendLine()
+
+ appendLine(message.content.parseAsHtml())
+
+ if (!attachments.isNullOrEmpty()) {
+ appendLine()
+ appendLine("Załączniki:")
+
+ append(attachments.orEmpty().joinToString(separator = "\n") { attachment ->
+ "${attachment.filename}: ${attachment.url}"
+ })
}
-
- view?.shareText(
- text,
- "FW: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}"
- )
- return true
}
- return false
+
+ view?.shareText(
+ subject = "FW: $subject",
+ text = text,
+ )
+ return true
}
@SuppressLint("NewApi")
fun onPrint(): Boolean {
- 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 message = message ?: return false
+ val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
- val messageContent = "${it.content}
"
- .replace(Regex("[\\n\\r]{2,}"), "")
- .replace(Regex("[\\n\\r]"), "
")
+ val dateString = message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
- val jobName = "Wiadomość " + when {
- it.sender.isNotEmpty() -> "od ${it.sender}"
- else -> "do ${it.recipient}"
- } + " $dateString: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }} | Wulkanowy"
+ val infoContent = buildString {
+ append("
Data wysłania
$dateString")
- view?.apply {
- val html = printHTML
- .replace(
- "%SUBJECT%",
- it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() })
- .replace("%CONTENT%", messageContent)
- .replace("%INFO%", infoContent)
- printDocument(html, jobName)
- }
- return true
+ append("Od
${message.sender}")
+ append("DO
${message.recipients}")
}
- return false
+ val messageContent = "${message.content}
"
+ .replace(Regex("[\\n\\r]{2,}"), "")
+ .replace(Regex("[\\n\\r]"), "
")
+
+ val jobName = buildString {
+ append("Wiadomość ")
+ append("od ${message.correspondents}")
+ append("do ${message.correspondents}")
+ append(" $dateString: $subject | Wulkanowy")
+ }
+
+ view?.apply {
+ val html = printHTML
+ .replace("%SUBJECT%", subject)
+ .replace("%CONTENT%", messageContent)
+ .replace("%INFO%", infoContent)
+ printDocument(html, jobName)
+ }
+
+ return true
}
private fun deleteMessage() {
@@ -168,16 +178,17 @@ class MessagePreviewPresenter @Inject constructor(
view?.run {
showContent(false)
showProgress(true)
- showOptions(false)
+ showOptions(show = false, isReplayable = false)
showErrorView(false)
}
- Timber.i("Delete message ${message?.id}")
+ Timber.i("Delete message ${message?.messageGlobalKey}")
presenterScope.launch {
runCatching {
- val student = studentRepository.getCurrentStudent()
- messageRepository.deleteMessage(student, message!!)
+ val student = studentRepository.getCurrentStudent(decryptPass = true)
+ val mailbox = mailboxRepository.getMailbox(student)
+ messageRepository.deleteMessage(student, mailbox, message!!)
}
.onFailure {
retryCallback = { onMessageDelete() }
@@ -211,7 +222,10 @@ class MessagePreviewPresenter @Inject constructor(
private fun initOptions() {
view?.apply {
- showOptions(message != null)
+ showOptions(
+ show = message != null,
+ isReplayable = message?.folderId != MessageFolder.SENT.id,
+ )
message?.let {
when (it.folderId == MessageFolder.TRASHED.id) {
true -> setDeletedOptionsLabels()
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 88fe77d94..c5a947939 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
@@ -28,7 +28,7 @@ interface MessagePreviewView : BaseView {
fun setErrorRetryCallback(callback: () -> Unit)
- fun showOptions(show: Boolean)
+ fun showOptions(show: Boolean, isReplayable: Boolean)
fun setDeletedOptionsLabels()
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt
index 70f9a9b54..334e389e2 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt
@@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
+import android.text.Spanned
import android.view.Menu
import android.view.MenuItem
import android.view.TouchDelegate
@@ -13,11 +14,12 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
+import androidx.core.text.parseAsHtml
+import androidx.core.text.toHtml
import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
-import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.databinding.ActivitySendMessageBinding
import io.github.wulkanowy.ui.base.BaseActivity
import io.github.wulkanowy.utils.dpToPx
@@ -72,17 +74,32 @@ class SendMessageActivity : BaseActivity
formSubjectValue = binding.sendMessageSubject.text.toString()
- formContentValue = binding.sendMessageMessageContent.text.toString()
+ formContentValue =
+ binding.sendMessageMessageContent.text.toString().parseAsHtml().toString()
presenter.onAttachView(
view = this,
@@ -110,7 +127,7 @@ class SendMessageActivity : BaseActivity) {
@@ -165,7 +182,7 @@ class SendMessageActivity : BaseActivity "Re: "
+ true -> "RE: "
else -> "FW: "
} + message.subject
)
if (preferencesRepository.fillMessageContent || reply != true) {
- setContent(
- when (reply) {
- true -> "\n\n"
- else -> ""
- } + when (message.sender.isNotEmpty()) {
- true -> "Od: ${message.sender}\n"
- false -> "Do: ${message.recipient}\n"
- } + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}"
- )
+ setContent(buildString {
+ if (reply == true) {
+ append("
")
+ }
+
+ append("Od: ${message.sender}
")
+ append("Do: ${message.recipients}
")
+ append("Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}
")
+ append(message.content)
+ })
}
}
}
@@ -111,21 +113,24 @@ class SendMessagePresenter @Inject constructor(
private fun loadData(message: Message?, reply: Boolean?) {
resourceFlow {
val student = studentRepository.getCurrentStudent()
- val semester = semesterRepository.getCurrentSemester(student)
- val unit = reportingUnitRepository.getReportingUnit(student, semester.unitId)
+ val mailbox = mailboxRepository.getMailbox(student)
Timber.i("Loading recipients started")
- val recipients = when {
- unit != null -> recipientRepository.getRecipients(student, unit, 2)
- else -> listOf()
- }.let { createChips(it) }
+ val recipients = createChips(
+ recipients = recipientRepository.getRecipients(
+ student = student,
+ mailbox = mailbox,
+ type = MailboxType.EMPLOYEE,
+ )
+ )
Timber.i("Loading recipients result: Success, fetched %d recipients", recipients.size)
Timber.i("Loading message recipients started")
val messageRecipients = when {
- message != null && reply == true -> recipientRepository.getMessageRecipients(
- student,
- message
+ message != null && reply == true -> recipientRepository.getMessageSender(
+ student = student,
+ message = message,
+ mailbox = mailbox,
)
else -> emptyList()
}.let { createChips(it) }
@@ -134,7 +139,7 @@ class SendMessagePresenter @Inject constructor(
messageRecipients.size
)
- Triple(unit, recipients, messageRecipients)
+ Triple(mailbox, recipients, messageRecipients)
}
.logResourceStatus("load recipients")
.onEach {
@@ -143,19 +148,14 @@ class SendMessagePresenter @Inject constructor(
showProgress(true)
showContent(false)
}
- is Resource.Success -> it.data.let { (reportingUnit, recipientChips, selectedRecipientChips) ->
+ is Resource.Success -> it.data.let { (mailbox, recipientChips, selectedRecipientChips) ->
view?.run {
- if (reportingUnit != null) {
- setReportingUnit(reportingUnit)
- setRecipients(recipientChips)
- if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(
- selectedRecipientChips
- )
- showContent(true)
- } else {
- Timber.i("Loading recipients result: Can't find the reporting unit")
- view?.showEmpty(true)
- }
+ setMailbox(getMailboxName(mailbox))
+ setRecipients(recipientChips)
+ if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(
+ selectedRecipientChips
+ )
+ showContent(true)
}
}
is Resource.Error -> {
@@ -171,7 +171,14 @@ class SendMessagePresenter @Inject constructor(
private fun sendMessage(subject: String, content: String, recipients: List) {
resourceFlow {
val student = studentRepository.getCurrentStudent()
- messageRepository.sendMessage(student, subject, content, recipients)
+ val mailbox = mailboxRepository.getMailbox(student)
+ messageRepository.sendMessage(
+ student = student,
+ subject = subject,
+ content = content,
+ recipients = recipients,
+ mailboxId = mailbox.globalKey,
+ )
}.logResourceStatus("sending message").onEach {
when (it) {
is Resource.Loading -> view?.run {
@@ -201,31 +208,44 @@ class SendMessagePresenter @Inject constructor(
}
private fun createChips(recipients: List): List {
- fun generateCorrectSummary(recipientRealName: String): String {
- val substring = recipientRealName.substringBeforeLast("-")
- return when {
- substring == recipientRealName -> recipientRealName
- substring.indexOf("(") != -1 -> {
- recipientRealName.indexOf("(")
- .let { recipientRealName.substring(if (it != -1) it else 0) }
- }
- substring.indexOf("[") != -1 -> {
- recipientRealName.indexOf("[")
- .let { recipientRealName.substring(if (it != -1) it else 0) }
- }
- else -> recipientRealName.substringAfter("-")
- }.trim()
- }
-
return recipients.map {
RecipientChipItem(
- title = it.name,
- summary = generateCorrectSummary(it.realName),
+ title = it.userName,
+ summary = buildString {
+ getMailboxType(it.type)?.let(::append)
+ if (isNotBlank()) append(" ")
+
+ append("(${it.schoolShortName})")
+ },
recipient = it
)
}
}
+ private fun getMailboxName(mailbox: Mailbox): String {
+ return buildString {
+ append(mailbox.userName)
+ append(" - ")
+ append(getMailboxType(mailbox.type))
+
+ if (mailbox.type == MailboxType.PARENT) {
+ append(" - ")
+ append(mailbox.studentName)
+ }
+
+ append(" - ")
+ append("(${mailbox.schoolNameShort})")
+ }
+ }
+
+ private fun getMailboxType(type: MailboxType): String? = when (type) {
+ MailboxType.STUDENT -> view?.mailboxStudent
+ MailboxType.PARENT -> view?.mailboxParent
+ MailboxType.GUARDIAN -> view?.mailboxGuardian
+ MailboxType.EMPLOYEE -> view?.mailboxEmployee
+ MailboxType.UNKNOWN -> null
+ }
+
fun onMessageContentChange() {
presenterScope.launch {
messageUpdateChannel.send(Unit)
@@ -263,7 +283,7 @@ class SendMessagePresenter @Inject constructor(
fun getRecipientsNames(): String {
return messageRepository.draftMessage?.recipients.orEmpty()
- .joinToString { it.recipient.name }
+ .joinToString { it.recipient.userName }
}
fun clearDraft() {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt
index 21b42e3e4..1057114b8 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt
@@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.message.send
-import io.github.wulkanowy.data.db.entities.ReportingUnit
+import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.ui.base.BaseView
interface SendMessageView : BaseView {
@@ -18,9 +18,17 @@ interface SendMessageView : BaseView {
val messageSuccess: String
+ val mailboxStudent: String
+
+ val mailboxParent: String
+
+ val mailboxGuardian: String
+
+ val mailboxEmployee: String
+
fun initView()
- fun setReportingUnit(unit: ReportingUnit)
+ fun setMailbox(mailbox: String)
fun setRecipients(recipients: List)
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 af0923b94..55f03ef84 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
@@ -8,7 +8,6 @@ import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
-import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.databinding.ItemMessageBinding
import io.github.wulkanowy.databinding.ItemMessageChipsBinding
import io.github.wulkanowy.utils.toFormattedString
@@ -88,12 +87,8 @@ class MessageTabAdapter @Inject constructor() :
with(holder.binding) {
val style = if (message.unread) Typeface.BOLD else Typeface.NORMAL
- messageItemAuthor.run {
- text = if (message.folderId == MessageFolder.SENT.id) {
- message.recipient
- } else {
- message.sender
- }
+ with(messageItemAuthor) {
+ text = message.correspondents
setTypeface(null, style)
}
messageItemSubject.run {
@@ -145,7 +140,7 @@ class MessageTabAdapter @Inject constructor() :
val newItem = new[newItemPosition]
return if (oldItem is MessageTabDataItem.MessageItem && newItem is MessageTabDataItem.MessageItem) {
- oldItem.message.id == newItem.message.id
+ oldItem.message.messageGlobalKey == newItem.message.messageGlobalKey
} else {
oldItem.viewType == newItem.viewType
}
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 870b6433e..54711a689 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
@@ -3,8 +3,8 @@ package io.github.wulkanowy.ui.modules.message.tab
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.enums.MessageFolder
+import io.github.wulkanowy.data.repositories.MailboxRepository
import io.github.wulkanowy.data.repositories.MessageRepository
-import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
@@ -26,7 +26,7 @@ class MessageTabPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val messageRepository: MessageRepository,
- private val semesterRepository: SemesterRepository,
+ private val mailboxRepository: MailboxRepository,
private val analytics: AnalyticsHelper
) : BasePresenter(errorHandler, studentRepository) {
@@ -122,7 +122,8 @@ class MessageTabPresenter @Inject constructor(
runCatching {
val student = studentRepository.getCurrentStudent(true)
- messageRepository.deleteMessages(student, messageList)
+ val mailbox = mailboxRepository.getMailbox(student)
+ messageRepository.deleteMessages(student, mailbox, messageList)
}
.onFailure(errorHandler::dispatch)
.onSuccess { view?.showMessagesDeleted() }
@@ -159,7 +160,7 @@ class MessageTabPresenter @Inject constructor(
}
fun onMessageItemSelected(messageItem: MessageTabDataItem.MessageItem, position: Int) {
- Timber.i("Select message ${messageItem.message.id} item (position: $position)")
+ Timber.i("Select message ${messageItem.message.messageGlobalKey} item (position: $position)")
if (!isActionMode) {
view?.run {
@@ -206,8 +207,8 @@ class MessageTabPresenter @Inject constructor(
flatResourceFlow {
val student = studentRepository.getCurrentStudent()
- val semester = semesterRepository.getCurrentSemester(student)
- messageRepository.getMessages(student, semester, folder, forceRefresh)
+ val mailbox = mailboxRepository.getMailbox(student)
+ messageRepository.getMessages(student, mailbox, folder, forceRefresh)
}
.logResourceStatus("load $folder message")
.onResourceData {
@@ -333,7 +334,7 @@ class MessageTabPresenter @Inject constructor(
addAll(data.map { message ->
MessageTabDataItem.MessageItem(
message = message,
- isSelected = messagesToDelete.any { it.id == message.id },
+ isSelected = messagesToDelete.any { it.messageGlobalKey == message.messageGlobalKey },
isActionMode = isActionMode
)
})
@@ -345,10 +346,9 @@ class MessageTabPresenter @Inject constructor(
private fun calculateMatchRatio(message: Message, query: String): Int {
val subjectRatio = FuzzySearch.tokenSortPartialRatio(query.lowercase(), message.subject)
- val senderOrRecipientRatio = FuzzySearch.tokenSortPartialRatio(
+ val correspondentsRatio = FuzzySearch.tokenSortPartialRatio(
query.lowercase(),
- if (message.sender.isNotEmpty()) message.sender.lowercase()
- else message.recipient.lowercase()
+ message.correspondents
)
val dateRatio = listOf(
@@ -364,7 +364,7 @@ class MessageTabPresenter @Inject constructor(
return (subjectRatio.toDouble().pow(2)
- + senderOrRecipientRatio.toDouble().pow(2)
+ + correspondentsRatio.toDouble().pow(2)
+ dateRatio.toDouble().pow(2) * 2
).toInt()
}
diff --git a/app/src/main/res/layout/activity_send_message.xml b/app/src/main/res/layout/activity_send_message.xml
index 10b581f77..320782bdc 100644
--- a/app/src/main/res/layout/activity_send_message.xml
+++ b/app/src/main/res/layout/activity_send_message.xml
@@ -16,8 +16,7 @@
app:layout_constraintBottom_toTopOf="@id/sendMessageScroll"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- tools:targetApi="lollipop" />
+ app:layout_constraintTop_toTopOf="parent" />
Move to trash
Delete permanently
Message deleted successfully
+ student
+ parent
+ guardian
+ employee
Share
Print
Subject
@@ -300,7 +304,6 @@
Only unread
Only with attachments
Read: %s
- Read by: %1$d of %2$d people
- %1$d message
- %1$d messages
diff --git a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
index 225399306..ff0a53135 100644
--- a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
+++ b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
@@ -1,5 +1,7 @@
package io.github.wulkanowy
+import io.github.wulkanowy.data.db.entities.Mailbox
+import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
@@ -21,6 +23,16 @@ fun getSemesterEntity(diaryId: Int = 1, semesterId: Int = 1, start: LocalDate =
end = end
)
+fun getMailboxEntity() = Mailbox(
+ globalKey = "v4",
+ fullName = "",
+ userName = "",
+ userLoginId = 0,
+ studentName = "",
+ schoolNameShort = "",
+ type = MailboxType.UNKNOWN,
+)
+
fun getSemesterPojo(diaryId: Int, semesterId: Int, start: LocalDate, end: LocalDate, semesterName: Int = 1) = SdkSemester(
diaryId = diaryId,
kindergartenDiaryId = 0,
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
index 2a5d2e2b4..24306bfeb 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
@@ -10,12 +10,10 @@ import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.toFirstResult
-import io.github.wulkanowy.getSemesterEntity
+import io.github.wulkanowy.getMailboxEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Folder
-import io.github.wulkanowy.sdk.pojo.MessageDetails
-import io.github.wulkanowy.sdk.pojo.Sender
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.Status
import io.github.wulkanowy.utils.status
@@ -23,7 +21,6 @@ import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
@@ -60,7 +57,7 @@ class MessageRepositoryTest {
private val student = getStudentEntity()
- private val semester = getSemesterEntity()
+ private val mailbox = getMailboxEntity()
private lateinit var repository: MessageRepository
@@ -80,59 +77,18 @@ class MessageRepositoryTest {
)
}
- @Test
- fun `get messages when read by values was changed on already read message`() = runTest {
- every { messageDb.loadAll(any(), any()) } returns flow {
- val dbMessage = getMessageEntity(3, "", false).apply {
- unreadBy = 10
- readBy = 5
- isNotified = true
- }
- emit(listOf(dbMessage))
- }
- coEvery { sdk.getMessages(Folder.RECEIVED, any(), any()) } returns listOf(
- getMessageDto(messageId = 3, content = "", unread = false).copy(
- unreadBy = 5,
- readBy = 10,
- )
- )
- coEvery { messageDb.deleteAll(any()) } just Runs
- coEvery { messageDb.insertAll(any()) } returns listOf()
-
- repository.getMessages(
- student = student,
- semester = semester,
- folder = MessageFolder.RECEIVED,
- forceRefresh = true,
- notify = true, // all new messages will be marked as not notified
- ).toFirstResult().dataOrNull.orEmpty()
-
- coVerify(exactly = 1) { messageDb.deleteAll(emptyList()) }
- coVerify(exactly = 1) { messageDb.insertAll(emptyList()) }
- coVerify(exactly = 1) {
- messageDb.updateAll(withArg {
- assertEquals(1, it.size)
- assertEquals(5, it.single().unreadBy)
- assertEquals(10, it.single().readBy)
- })
- }
- }
-
@Test
fun `get messages when fetched completely new message without notify`() = runBlocking {
every { messageDb.loadAll(any(), any()) } returns flowOf(emptyList())
- coEvery { sdk.getMessages(Folder.RECEIVED, any(), any()) } returns listOf(
- getMessageDto(messageId = 4, content = "Test", unread = true).copy(
- unreadBy = 5,
- readBy = 10,
- )
+ coEvery { sdk.getMessages(Folder.RECEIVED, any()) } returns listOf(
+ getMessageDto()
)
coEvery { messageDb.deleteAll(any()) } just Runs
coEvery { messageDb.insertAll(any()) } returns listOf()
repository.getMessages(
student = student,
- semester = semester,
+ mailbox = mailbox,
folder = MessageFolder.RECEIVED,
forceRefresh = true,
notify = false,
@@ -151,7 +107,7 @@ class MessageRepositoryTest {
fun `throw error when message is not in the db`() {
val testMessage = getMessageEntity(1, "", false)
coEvery {
- messageDb.loadMessageWithAttachment(1, 1)
+ messageDb.loadMessageWithAttachment("v4")
} throws NoSuchElementException("No message in database")
runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
@@ -162,7 +118,7 @@ class MessageRepositoryTest {
val testMessage = getMessageEntity(123, "Test", false)
val messageWithAttachment = MessageWithAttachment(testMessage, emptyList())
- coEvery { messageDb.loadMessageWithAttachment(1, testMessage.messageId) } returns flowOf(
+ coEvery { messageDb.loadMessageWithAttachment("v4") } returns flowOf(
messageWithAttachment
)
@@ -174,7 +130,7 @@ class MessageRepositoryTest {
}
@Test
- fun `get message when content in db is empty`() {
+ fun `get message when content in db is empty`() = runTest {
val testMessage = getMessageEntity(123, "", true)
val testMessageWithContent = testMessage.copy().apply { content = "Test" }
@@ -182,23 +138,19 @@ class MessageRepositoryTest {
val mWaWithContent = MessageWithAttachment(testMessageWithContent, emptyList())
coEvery {
- messageDb.loadMessageWithAttachment(
- 1,
- testMessage.messageId
- )
+ messageDb.loadMessageWithAttachment("v4")
} returnsMany listOf(flowOf(mWa), flowOf(mWaWithContent))
coEvery {
- sdk.getMessageDetails(
- messageId = testMessage.messageId,
- folderId = 1,
- read = false,
- id = testMessage.realId
- )
- } returns MessageDetails("Test", emptyList())
+ sdk.getMessageDetails("v4")
+ } returns mockk {
+ every { sender } returns ""
+ every { recipients } returns listOf("")
+ every { attachments } returns listOf()
+ }
coEvery { messageDb.updateAll(any()) } just Runs
coEvery { messageAttachmentDao.insertAttachments(any()) } returns listOf(1)
- val res = runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
+ val res = repository.getMessage(student, testMessage).toFirstResult()
assertEquals(null, res.errorOrNull)
assertEquals(Status.SUCCESS, res.status)
@@ -211,7 +163,7 @@ class MessageRepositoryTest {
val testMessage = getMessageEntity(123, "", false)
coEvery {
- messageDb.loadMessageWithAttachment(1, testMessage.messageId)
+ messageDb.loadMessageWithAttachment("v4")
} throws UnknownHostException()
runBlocking { repository.getMessage(student, testMessage).toFirstResult() }
@@ -222,7 +174,7 @@ class MessageRepositoryTest {
val testMessage = getMessageEntity(123, "", true)
coEvery {
- messageDb.loadMessageWithAttachment(1, testMessage.messageId)
+ messageDb.loadMessageWithAttachment("v4")
} throws UnknownHostException()
runBlocking { repository.getMessage(student, testMessage).toList()[1] }
@@ -233,42 +185,30 @@ class MessageRepositoryTest {
content: String,
unread: Boolean
) = Message(
- studentId = 1,
- realId = 1,
+ messageGlobalKey = "v4",
+ mailboxKey = "",
+ correspondents = "",
messageId = messageId,
- sender = "",
- senderId = 0,
- recipient = "Wielu adresatów",
subject = "",
date = Instant.EPOCH,
folderId = 1,
unread = unread,
- removed = false,
hasAttachments = false
).apply {
this.content = content
- unreadBy = 1
- readBy = 1
}
- private fun getMessageDto(
- messageId: Int,
- content: String,
- unread: Boolean,
- ) = io.github.wulkanowy.sdk.pojo.Message(
- id = 1,
- messageId = messageId,
- sender = Sender("", "", 0, 0, 0, ""),
+ private fun getMessageDto() = io.github.wulkanowy.sdk.pojo.Message(
+ globalKey = "v4",
+ mailbox = "",
+ correspondents = "",
+ id = 4,
recipients = listOf(),
subject = "",
- content = content,
- date = Instant.EPOCH.atZone(ZoneOffset.UTC).toLocalDateTime(),
+ content = "Test",
dateZoned = Instant.EPOCH.atZone(ZoneOffset.UTC),
folderId = 1,
- unread = unread,
- unreadBy = 0,
- readBy = 0,
- removed = false,
+ unread = true,
hasAttachments = false,
)
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt
index 980abac0a..ae73a7958 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt
@@ -1,19 +1,15 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.RecipientDao
-import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.mappers.mapToEntities
+import io.github.wulkanowy.getMailboxEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
+import io.github.wulkanowy.sdk.pojo.MailboxType
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.mockk.MockKAnnotations
-import io.mockk.Runs
-import io.mockk.coEvery
-import io.mockk.coVerify
-import io.mockk.every
+import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
-import io.mockk.just
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -36,9 +32,30 @@ class RecipientLocalTest {
private lateinit var recipientRepository: RecipientRepository
private val remoteList = listOf(
- SdkRecipient("2rPracownik", "Kowalski Jan", 3, 4, 2, "hash", "Kowalski Jan [KJ] - Pracownik (Fake123456)"),
- SdkRecipient("3rPracownik", "Kowalska Karolina", 4, 4, 2, "hash", "Kowalska Karolina [KK] - Pracownik (Fake123456)"),
- SdkRecipient("4rPracownik", "Krupa Stanisław", 5, 4, 1, "hash", "Krupa Stanisław [KS] - Uczeń (Fake123456)")
+ SdkRecipient(
+ mailboxGlobalKey = "2rPracownik",
+ userName = "Kowalski Jan",
+ fullName = "Kowalski Jan [KJ] - Pracownik (Fake123456)",
+ studentName = "",
+ schoolNameShort = "",
+ type = MailboxType.UNKNOWN,
+ ),
+ SdkRecipient(
+ mailboxGlobalKey = "3rPracownik",
+ userName = "Kowalska Karolina",
+ fullName = "Kowalska Karolina [KK] - Pracownik (Fake123456)",
+ studentName = "",
+ schoolNameShort = "",
+ type = MailboxType.UNKNOWN,
+ ),
+ SdkRecipient(
+ mailboxGlobalKey = "4rPracownik",
+ userName = "Krupa Stanisław",
+ fullName = "Krupa Stanisław [KS] - Uczeń (Fake123456)",
+ studentName = "",
+ schoolNameShort = "",
+ type = MailboxType.UNKNOWN,
+ )
)
@Before
@@ -52,39 +69,61 @@ class RecipientLocalTest {
@Test
fun `load recipients when items already in database`() {
// prepare
- coEvery { recipientDb.loadAll(4, 123, 7) } returnsMany listOf(
- remoteList.mapToEntities(4),
- remoteList.mapToEntities(4)
+ coEvery { recipientDb.loadAll(io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, "v4") } returnsMany listOf(
+ remoteList.mapToEntities("v4"),
+ remoteList.mapToEntities("v4")
)
coEvery { recipientDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { recipientDb.deleteAll(any()) } just Runs
// execute
- val res = runBlocking { recipientRepository.getRecipients(student, ReportingUnit(4, 123, "", 4, "", listOf()), 7) }
+ val res = runBlocking {
+ recipientRepository.getRecipients(
+ student = student,
+ mailbox = getMailboxEntity(),
+ type = io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
+ )
+ }
// verify
assertEquals(3, res.size)
- coVerify { recipientDb.loadAll(4, 123, 7) }
+ coVerify {
+ recipientDb.loadAll(
+ type = io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
+ studentMailboxGlobalKey = "v4"
+ )
+ }
}
@Test
fun `load recipients when database is empty`() {
// prepare
- coEvery { sdk.getRecipients(123, 7) } returns remoteList
- coEvery { recipientDb.loadAll(4, 123, 7) } returnsMany listOf(
+ coEvery { sdk.getRecipients("v4") } returns remoteList
+ coEvery {
+ recipientDb.loadAll(
+ io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
+ "v4"
+ )
+ } returnsMany listOf(
emptyList(),
- remoteList.mapToEntities(4)
+ remoteList.mapToEntities("v4")
)
coEvery { recipientDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { recipientDb.deleteAll(any()) } just Runs
// execute
- val res = runBlocking { recipientRepository.getRecipients(student, ReportingUnit(4, 123, "", 4, "", listOf()), 7) }
+ val res = runBlocking {
+ recipientRepository.getRecipients(
+ student = student,
+ mailbox = getMailboxEntity(),
+ type = io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
+ )
+ }
// verify
assertEquals(3, res.size)
- coVerify { sdk.getRecipients(123, 7) }
- coVerify { recipientDb.loadAll(4, 123, 7) }
+ coVerify { sdk.getRecipients("v4") }
+ coVerify { recipientDb.loadAll(io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, "v4") }
coVerify { recipientDb.insertAll(match { it.isEmpty() }) }
coVerify { recipientDb.deleteAll(match { it.isEmpty() }) }
}
From bc0689a30df9ac9c7b35a802256e1922dbbe8264 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 22 Aug 2022 13:28:27 +0000
Subject: [PATCH 191/669] Bump coil from 2.1.0 to 2.2.0 (#1949)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 0efb9c303..d86f5f430 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -236,7 +236,7 @@ dependencies {
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
- implementation "io.coil-kt:coil:2.1.0"
+ implementation "io.coil-kt:coil:2.2.0"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
From d9e22af5ef0672de79b122af8dfca08b5743ca49 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 22 Aug 2022 13:28:58 +0000
Subject: [PATCH 192/669] Bump desugar_jdk_libs from 1.1.5 to 1.1.6 (#1948)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index d86f5f430..efa9b3e09 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -188,7 +188,7 @@ ext {
dependencies {
implementation "io.github.wulkanowy:sdk:dbe87aac"
- coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.6'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
From 71f1a55437e3b447b8d3e6b4fa87713c3f382027 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Mon, 22 Aug 2022 16:45:59 +0200
Subject: [PATCH 193/669] New Crowdin updates (#1895)
---
.../main/res/values-cs/preferences_values.xml | 2 +-
app/src/main/res/values-cs/strings.xml | 14 ++++++++++++--
.../main/res/values-de/preferences_values.xml | 2 +-
app/src/main/res/values-de/strings.xml | 14 ++++++++++++--
app/src/main/res/values-pl/strings.xml | 16 +++++++++++++---
app/src/main/res/values-ru/strings.xml | 14 ++++++++++++--
.../main/res/values-sk/preferences_values.xml | 2 +-
app/src/main/res/values-sk/strings.xml | 16 +++++++++++++---
app/src/main/res/values-uk/strings.xml | 14 ++++++++++++--
9 files changed, 77 insertions(+), 17 deletions(-)
diff --git a/app/src/main/res/values-cs/preferences_values.xml b/app/src/main/res/values-cs/preferences_values.xml
index c8731372f..23073adf1 100644
--- a/app/src/main/res/values-cs/preferences_values.xml
+++ b/app/src/main/res/values-cs/preferences_values.xml
@@ -34,7 +34,7 @@
- Abecedně
- Podle data
- - By average
+ - Podle průměru
- Dzienniczek+
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 11054a0e5..5f76bb6e1 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -77,6 +77,9 @@
Přihlásit se
Relace vypršela
Relace vypršela. Přihlaste se prosím znovu
+ Podpora aplikace
+ Líbí se Vám tato aplikace? Podpořte její vývoj tím, že povolíte neinvazivní reklamy, které můžete kdykoliv vypnout
+ Zapnout reklamy
Známka
Semestr %d
@@ -94,7 +97,7 @@
Předpokládaná známka
Vypočítaný průměr
Jak funguje vypočítaný průměr?
- Vypočítaný průměr je aritmetický průměr vypočítaný z průměrů předmětů. Umožňuje vám to znát přibližný konečný průměr. Vypočítává se způsobem zvoleným uživatelem v nastavení aplikaci. Doporučuje se vybrat příslušnou možnost. Důvodem je rozdílný výpočet školních průměrů. Pokud vaše škola navíc uvádí průměr předmětů na stránce deníku Vulcan, aplikace si je stáhne a tyto průměry nepočítá. To lze změnit vynucením výpočtu průměru v nastavení aplikaci.\n\nPrůměr známek pouze z vybraného semestru:\n1. Výpočet váženého průměru pro každý předmět v daném semestru\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů\n\nPrůměr průměrů z obou semestrů:\n1. Výpočet váženého průměru pro každý předmět v semestru 1 a 2\n2. Výpočet aritmetického průměru vypočítaných průměrů za semestry 1 a 2 pro každý předmět.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru sečtených průměrů\n\nPrůměr známek z celého roku:\n1. Výpočet váženého průměru za rok pro každý předmět. Konečný průměr v 1. semestru je nepodstatný.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru součtených průměrů
+ Vypočítaný průměr je aritmetický průměr vypočítaný z průměrů předmětů. Umožňuje vám to znát přibližný konečný průměr. Vypočítává se způsobem zvoleným uživatelem v nastavení aplikaci. Doporučuje se vybrat příslušnou možnost. Důvodem je rozdílný výpočet školních průměrů. Pokud vaše škola navíc uvádí průměr předmětů na stránce deníku Vulcan, aplikace si je stáhne a tyto průměry nepočítá. To lze změnit vynucením výpočtu průměru v nastavení aplikaci.\n\nPrůměr známek pouze z vybraného semestru:\n1. Výpočet váženého průměru pro každý předmět v daném semestru\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů\n\nPrůměr průměrů z obou semestrů:\n1. Výpočet váženého průměru pro každý předmět v semestru 1 a 2\n2. Výpočet aritmetického průměru vypočítaných průměrů za semestry 1 a 2 pro každý předmět.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru sečtených průměrů\n\nPrůměr známek z celého roku:\n1. Výpočet váženého průměru za rok pro každý předmět. Konečný průměr v 1. semestru je nepodstatný.\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů
Jak funguje konečný průměr?
Konečný průměr je aritmetický průměr vypočítaný ze všech aktuálně dostupných konečných známek v daném semestru.\n\nSchéma výpočtu se skládá z následujících kroků:\n1. Sčítání konečných známek zadaných učiteli\n2. Děleno počtem předmětů, pro které už byly uděleny známky
Konečný průměr
@@ -294,6 +297,10 @@
Přesunout do koše
Odstranit natrvalo
Zpráva byla úspěšně odstraněna
+ žák
+ rodič
+ opatrovník
+ pracovník
Sdílet
Vytisknout
Předmět
@@ -305,7 +312,6 @@
Pouze nepřečtené
Pouze s přílohami
Přečtena: %s
- Přečtena přes: %1$d z %2$d osob
- %1$d zpráva
- %1$d zprávy
@@ -721,6 +727,10 @@
Ochrana osobních údajů
Reklama se načítá
Děkujeme za vaši podporu, vraťte se později pro více reklam
+ Můžeme použít Vaše data k zobrazení reklam?
+ Volbu můžete kdykoliv změnit v nastavení aplikace. Můžeme použít Vaše data k zobrazení reklam šitých pro vás nebo pomocí méně vašich dat zobrazovat nepřizpůsobené reklamy. Podrobnosti naleznete v našich Zásadách ochrany osobních údajů
+ Přizpůsobené reklamy
+ Nepřizpůsobené reklamy
Pokročilé
Vzhled a chování
Oznámení
diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml
index 097e90e96..d9cac1959 100644
--- a/app/src/main/res/values-de/preferences_values.xml
+++ b/app/src/main/res/values-de/preferences_values.xml
@@ -34,7 +34,7 @@
- Alphabetisch
- Nach Datum
- - By average
+ - Nach Durchschnitt
- Dzienniczek+
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 86308aa19..ef79cee12 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -77,6 +77,9 @@
Anmelden
Die Sitzung ist abgelaufen
Die Sitzung ist abgelaufen, bitte loggen Sie sich erneut ein
+ Anwendungsunterstützung
+ Gefällt Ihnen diese App? Unterstützen Sie ihre Entwicklung, indem Sie nicht-invasive Werbung aktivieren, die Sie jederzeit deaktivieren können
+ Werbung aktivieren
Note
Semester %d
@@ -94,7 +97,7 @@
Vorhergesagte Note
Berechnender Durchschnitt
Wie funktioniert der berechnete Durchschnitt?
- Der berechnete Mittelwert ist das arithmetische Mittel, das aus den Durchschnittswerten der Probanden errechnet wird. Es erlaubt Ihnen, den ungefähre endgültigen Durchschnitt zu kennen. Sie wird auf eine vom Anwender in den Anwendungseinstellungen gewählte Weise berechnet. Es wird empfohlen, die entsprechende Option zu wählen. Das liegt daran, dass die Berechnung der Schuldurchschnitte unterschiedlich ist. Wenn Ihre Schule den Durchschnitt der Fächer auf der Vulcan-Seite angibt, lädt die Anwendung diese Fächer herunter und berechnet nicht den Durchschnitt. Dies kann geändert werden, indem die Berechnung des Durchschnitts in den Anwendungseinstellungen erzwungen wird. \n\nDurchschnitt der Noten nur aus dem ausgewählten Semester :\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in einem bestimmten Semester\n2. Addition der berechneten Durchschnittswerte\n3. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Durchschnitte aus beiden Semestern:\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in Semester 1 und 2\n2. Berechnung des arithmetischen Mittels der berechneten Durchschnitte für Semester 1 und 2 für jedes Fach. \n3. Hinzufügen von berechneten Durchschnittswerten\n4. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Noten aus dem ganzen Jahr:\n1. Berechnung des gewichteten Jahresdurchschnitts für jedes Fach. Der Abschlussdurchschnitt im 1. Semester ist irrelevant. \n3. Addition der berechneten Durchschnittswerte\n4. Berechnung des arithmetischen Mittels der summierten Mittelwerte
+ Der berechnete Mittelwert ist das arithmetische Mittel, das aus den Durchschnittswerten der Probanden errechnet wird. Es erlaubt Ihnen, den ungefähre endgültigen Durchschnitt zu kennen. Sie wird auf eine vom Anwender in den Anwendungseinstellungen gewählte Weise berechnet. Es wird empfohlen, die entsprechende Option zu wählen. Das liegt daran, dass die Berechnung der Schuldurchschnitte unterschiedlich ist. Wenn Ihre Schule den Durchschnitt der Fächer auf der Vulcan-Seite angibt, lädt die Anwendung diese Fächer herunter und berechnet nicht den Durchschnitt. Dies kann geändert werden, indem die Berechnung des Durchschnitts in den Anwendungseinstellungen erzwungen wird. \n\nDurchschnitt der Noten nur aus dem ausgewählten Semester :\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in einem bestimmten Semester\n2. Addition der berechneten Durchschnittswerte\n3. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Durchschnitte aus beiden Semestern:\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in Semester 1 und 2\n2. Berechnung des arithmetischen Mittels der berechneten Durchschnitte für Semester 1 und 2 für jedes Fach. \n3. Hinzufügen von berechneten Durchschnittswerten\n4. Berechnung des arithmetischen Mittels der summierten Durchschnitte\nDurchschnitt der Noten aus dem ganzen Jahr:\n1. Berechnung des gewichteten Jahresdurchschnitts für jedes Fach. Der Abschlussdurchschnitt im 1. Semester ist irrelevant. \n2. Addition der berechneten Durchschnittswerte\n3. Berechnung des arithmetischen Mittels der summierten Mittelwerte
Wie funktioniert der endgültige Durchschnitt?
Der Final Average ist das arithmetische Mittel, das aus allen derzeit verfügbaren Abschlussnoten des jeweiligen Semesters berechnet wird. \n\nDas Berechnungsschema besteht aus folgenden Schritten:\n1. Zusammenfassung der von den Lehrern gegebenen Abschlussnoten\n2. Division durch die Anzahl der Fächer, die bereits bewertet wurden
Finaler Durchschnitt
@@ -260,6 +263,10 @@
In Papierkorb verschieben
Dauerhaft löschen
Nachricht erfolgreich gelöscht
+ schüler
+ Eltern
+ Betreuer
+ Mitarbeiter
Teilen
Drucken
Thema
@@ -271,7 +278,6 @@
Nur ungelesen
Nur mit Anhängen
Lesen: %s
- Lesen von: %1$d von %2$d Personen
- %1$d Nachricht
- %1$d Nachrichten
@@ -633,6 +639,10 @@
Datenschutzerklärung
Anzeige wird geladen
Vielen Dank für Ihre Unterstützung, kommen Sie später wieder für weitere Anzeigen
+ Können wir Ihre Daten zur Anzeige von Werbung verwenden?
+ Sie können Ihre Wahl jederzeit in den App-Einstellungen ändern. Wir verwenden Ihre Daten, um auf Sie zugeschnittene Anzeigen anzuzeigen oder unter Verwendung weniger Ihrer Daten nicht personalisierte Werbung anzuzeigen. Bitte lesen Sie unsere Datenschutzerklärung für Details
+ Personalisierte Werbung
+ keine personalisierte Werbung
Erweitert
Aussehen & Verhalten
Benachrichtigungen
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 91e9fe7f3..566519e8c 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -77,6 +77,9 @@
Zaloguj się
Sesja wygasła
Sesja wygasła, zaloguj się ponownie
+ Wparcie aplikacji
+ Podoba Ci się ta aplikacja? Wspieraj jej rozwój poprzez włączenie nieinwazyjnych reklam, które możesz wyłączyć w dowolnym momencie
+ Włącz reklamy
Ocena
Semestr %d
@@ -94,7 +97,7 @@
Przewidywana ocena
Obliczona średnia
Jak działa obliczona średnia?
- Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\nŚrednia ocen tylko z wybranego semestru:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia ze średnich z obu semestrów:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semestrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia wszystkich ocen z całego roku:\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej z zsumowanych średnich
+ Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\nŚrednia ocen tylko z wybranego semestru:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia ze średnich z obu semestrów:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semestrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\nŚrednia wszystkich ocen z całego roku:\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej z zsumowanych średnich
Jak działa końcowa średnia?
Średnią końcową jest średnia arytmetyczna obliczona na podstawie wszystkich obecnie dostępnych ocen końcowych w danym semestrze.\n\nSchemat obliczeń składa się z następujących kroków:\n1. Sumowanie końcowych ocen wpisanych przez nauczycieli\n2. Dzielenie przez liczbę przedmiotów, z których oceny zostały już wystawione
Końcowa średnia
@@ -294,6 +297,10 @@
Przenieś do kosza
Usuń trwale
Wiadomość usunięta pomyślnie
+ uczeń
+ rodzic
+ opiekun
+ pracownik
Udostępnij
Drukuj
Temat
@@ -305,7 +312,6 @@
Tylko nieprzeczytane
Tylko z załącznikami
Przeczytana: %s
- Przeczytana przez: %1$d z %2$d osób
- %1$d wiadomość
- %1$d wiadomości
@@ -701,7 +707,7 @@
Przejdź do ustawień
Synchronizacja
Automatyczna aktualizacja
- Zawieszona na wakacjach
+ Wstrzymana podczas wakacji
Interwał aktualizacji
Tylko WiFi
Synchronizuj teraz
@@ -721,6 +727,10 @@
Polityka prywatności
Ładowanie reklamy
Dziękujemy za wsparcie, wróć później po więcej reklam
+ Czy możemy używać Twoich danych do wyświetlania reklam?
+ Możesz zmienić swój wybór w dowolnym momencie w ustawieniach aplikacji. Możemy wykorzystać Twoje dane do wyświetlania reklam dostosowanych do Ciebie lub, przy użyciu mniejszej ilości danych, wyświetlić niepersonalizowane reklamy. Zobacz naszą Politykę Prywatności, aby uzyskać więcej informacji
+ Spersonalizowane reklamy
+ Niespersonalizowane reklamy
Zaawansowane
Wygląd i zachowanie
Powiadomienia
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 662e0934f..a3c5a62df 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -77,6 +77,9 @@
Войти
Сеанс истёк
Сеанс истёк, авторизуйтесь снова
+ Application support
+ Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time
+ Enable ads
Оценка
%d семестр
@@ -94,7 +97,7 @@
Ожидаемая оценка
Рассчитанная средняя оценка
Как работает \"Рассчитанная средняя оценка\"?
- Рассчитанная средняя оценка - это среднее арифметическое, рассчитанное на основе средних оценок по предметам. Это позволяет узнать приблизительную итоговую среднюю оценку. Она рассчитывается способом, выбранным пользователем в настройках приложения. Рекомендуется выбрать подходящий вариант, так как каждая школа по разному считает среднюю оценку. Кроме того, если ваша школа выставляет средние оценки по предметам на странице Vulcan, приложение просто загрузит их. Это можно изменить, заставив приложение считать среднюю оценку в настройках.\n\nСредняя из оценок выбранного семестра:\n1. Вычисление средневзвешенного значения по каждому предмету за семестр\n2.Суммирование вычисленных значений\n3. Вычисление среднего арифметического суммированных значений\n\nСредняя из средних оценок семестров:\n1.Расчет средневзвешенного значения для каждого предмета в семестрах. \n2. Вычисление среднего арифметического из средневзвешенных значений для каждого предмета в семестрах.\n3. Суммирование средних арифметических\n4. Вычисление среднего арифматического из суммированных значений\n\nСредняя из оценок со всего года:\n1. Расчет средневзвешенного значения по каждому предмету за год. Итоговое среднее значение за 1 семестр не имеет значения.\n2. Суммирование вычисленных средних\n4. Расчет среднего арифметического суммированных чисел
+ The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages
Как работает \"Итоговая средняя оценка\"?
Итоговая средняя оценка - это среднее арифметическое, рассчитанное из всех имеющихся на данный момент итоговых оценок в семестре.\n\nРассчет происходит следующим образом:\n1. Суммирование итоговых оценок, выставленных преподавателями\n2. Полученная сумма делится на число предметов, по которым выставлены оценки
Итоговая средняя оценка
@@ -294,6 +297,10 @@
Перенести в корзину
Удалить навсегда
Письмо успешно удалено
+ student
+ parent
+ guardian
+ employee
Поделиться
Печать
Тема
@@ -305,7 +312,6 @@
Только непрочитанные
Только с вложениями
Прочитано: %s
- Прочитано: %1$d из %2$d человек
- %1$d сообщение
- %1$d сообщения
@@ -721,6 +727,10 @@
Политика конфиденциальности
Реклама загружается
Спасибо за вашу поддержку, возвращайтесь позже для дополнительной рекламы
+ Can we use your data to display ads?
+ You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details
+ Personalized ads
+ Non-personalized ads
Расширенные
Внешний вид и поведение
Уведомления
diff --git a/app/src/main/res/values-sk/preferences_values.xml b/app/src/main/res/values-sk/preferences_values.xml
index ab0a43b64..e4331315a 100644
--- a/app/src/main/res/values-sk/preferences_values.xml
+++ b/app/src/main/res/values-sk/preferences_values.xml
@@ -34,7 +34,7 @@
- Abecedne
- Podľa dátumu
- - By average
+ - Podľa priemeru
- Dzienniczek+
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 5ebd1e76b..6a4505d70 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -77,6 +77,9 @@
Prihlásiť sa
Relácia vypršala
Relácia vypršala. Prihláste sa prosím znovu
+ Podpora aplikácie
+ Páči sa Vám táto aplikácia? Podporte jej vývoj tým, že povolíte neinvazívne reklamy, ktoré môžete kedykoľvek vypnúť
+ Zapnúť reklamy
Známka
Semester %d
@@ -94,7 +97,7 @@
Predpokladaná známka
Vypočítaný priemer
Ako funguje vypočítaný priemer?
- Vypočítaný priemer je aritmetický priemer vypočítaný z priemerov predmetov. Umožňuje vám to poznať približný konečný priemer. Vypočítava sa spôsobom zvoleným užívateľom v nastaveniach aplikácii. Odporúča sa vybrať príslušnú možnosť. Dôvodom je rozdielny výpočet školských priemerov. Ak vaša škola navyše uvádza priemer predmetov na stránke denníka Vulcan, aplikácia si ich stiahne a tieto priemery nepočíta. To možno zmeniť vynútením výpočtu priemeru v nastavení aplikácii.\n\nPriemer známok iba z vybraného semestra:\n1. Výpočet váženého priemeru pre každý predmet v danom semestri\n2. Sčítanie vypočítaných priemerov\n3. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer priemerov z oboch semestrov:\n1. Výpočet váženého priemeru pre každý predmet v semestri 1 a 2\n2. Výpočet aritmetického priemeru vypočítaných priemerov za semestre 1 a 2 pre každý predmet.\n3. Sčítanie vypočítaných priemerov\n4. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer známok z celého roka:\n1. Výpočet váženého priemeru za rok pre každý predmet. Konečný priemer v 1. semestri je nepodstatný.\n3. Sčítanie vypočítaných priemerov\n4. Výpočet aritmetického priemeru součtených priemerov
+ Vypočítaný priemer je aritmetický priemer vypočítaný z priemerov predmetov. Umožňuje vám to poznať približný konečný priemer. Vypočítava sa spôsobom zvoleným užívateľom v nastaveniach aplikácii. Odporúča sa vybrať príslušnú možnosť. Dôvodom je rozdielny výpočet školských priemerov. Ak vaša škola navyše uvádza priemer predmetov na stránke denníka Vulcan, aplikácia si ich stiahne a tieto priemery nepočíta. To možno zmeniť vynútením výpočtu priemeru v nastavení aplikácii.\n\nPriemer známok iba z vybraného semestra:\n1. Výpočet váženého priemeru pre každý predmet v danom semestri\n2. Sčítanie vypočítaných priemerov\n3. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer priemerov z oboch semestrov:\n1. Výpočet váženého priemeru pre každý predmet v semestri 1 a 2\n2. Výpočet aritmetického priemeru vypočítaných priemerov za semestre 1 a 2 pre každý predmet.\n3. Sčítanie vypočítaných priemerov\n4. Výpočet aritmetického priemeru součtených priemerov\n\nPriemer známok z celého roka:\n1. Výpočet váženého priemeru za rok pre každý predmet. Konečný priemer v 1. semestri je nepodstatný.\n2. Sčítanie vypočítaných priemerov\n3. Výpočet aritmetického priemeru součtených priemerov
Ako funguje konečný priemer?
Konečný priemer je aritmetický priemer vypočítaný zo všetkých aktuálne dostupných konečných známok v danom semestri.\n\nSchéma výpočtu sa skladá z nasledujúcich krokov:\n1. Sčítanie konečných známok zadaných učiteľmi\n2. Delené počtom predmetov, pre ktoré už boli vydané známky
Konečný priemer
@@ -110,7 +113,7 @@
Váš priemer: %1$s
Vaša známka: %1$s
Trieda
- Žiák
+ Žiak
- %d známka
- %d známky
@@ -294,6 +297,10 @@
Presunúť do koša
Odstrániť natrvalo
Správa bola úspešne odstránená
+ žiak
+ rodič
+ opatrovník
+ pracovník
Zdieľať
Vytlačiť
Predmet
@@ -305,7 +312,6 @@
Iba neprečítané
Iba s prílohami
Prečítaná: %s
- Prečítaná cez: %1$d z %2$d osôb
- %1$d správa
- %1$d správy
@@ -721,6 +727,10 @@
Ochrana osobných údajov
Reklama sa načítava
Ďakujeme za vašu podporu, vráťte sa neskôr pre viac reklám
+ Môžeme použiť Vaše údaje na zobrazenie reklám?
+ Voľbu môžete kedykoľvek zmeniť v nastavení aplikácie. Môžeme použiť vaše údaje na zobrazenie reklám šitých pre vás alebo pomocou menej vašich dát zobrazovať neprispôsobené reklamy. Podrobnosti nájdete v našich Zásadách ochrany osobných údajov
+ Prispôsobené reklamy
+ Neprispôsobené reklamy
Pokročilé
Vzhľad a správanie
Oznámenia
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 0cc50dbe5..742e800f3 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -77,6 +77,9 @@
Увійти
Минув термін дії сесії
Минув термін дії сесії, авторизуйтеся знову
+ Application support
+ Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time
+ Enable ads
Оцінка
%d семестр
@@ -94,7 +97,7 @@
Передбачувана оцінка
Розрахована середня оцінка
Як працює \"Розрахована середня оцінка\"?
- Розрахована середня оцінка - це середнє арифметичне, обчислене з середніх оцінок з предметів.Це дозволяє дізнатися приблизну кінцеву середню оцінку.Вона розраховується способом, обраним користувачем у налаштуваннях програми.Рекомендується вибрати відповідний варіант, тому що кожна школа по різному розраховує середню оцінку.Крім того, якщо у вашій школі повідомляється середня оцінка з предметів на сторінці Vulcan, програма тільки завантажує ці оцінки.Це можна змінити шляхом примусового розрахунку середньоЇ оцінки в налаштуваннях програми.\n\nСередні оцінки тільки за обраний семестр:\n1. Розрахунок середньозваженого числа для кожного предмета в даному семестрі\n2. Сумування розрахованих числ\n3. Розрахунок середнього арифметичного з сумованих чисел\n\nСереднє значення з обох семестрів:\n1. Обчислення середньозваженого числа для кожного предмета у 1 та 2 семестрі\n2. Обчислення середнього арифметичного з розрахованих середньозважених числ за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахованих середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого числа за рік для кожного предмета. Підсумковий середній показник у 1-му семестрі не має значення.\n2. Сумування розрахованих середніх\n3. Обчислення середнього арифметичного з суммованих середніх
+ The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages
Як працює \"Підсумкова середня оцінка\"?
Підсумкове середнє значення - це середнє арифметичне, обчислене з усіх наявних наразі підсумкових оцінок у даному семестрі. \n\nСхема обчислення складається з таких кроків:\n1. Сумування підсумкових оцінок, виставленних викладачами\n2. Ділення на кількість предметів, з яких виставлені ці оцінки
Підсумкова середня оцінка
@@ -294,6 +297,10 @@
Перемістити до кошика
Видалити назавжди
Лист було успішно видалено
+ student
+ parent
+ guardian
+ employee
Поділитись
Друк
Тема
@@ -305,7 +312,6 @@
Лише непрочитані
Тільки з вкладеннями
Прочитаний: %s
- Прочитаний: %1$d з %2$d осіб
- %1$d лист
- %1$d листи
@@ -721,6 +727,10 @@
Політика конфіденційності
Реклама завантажується
Дякуємо за вашу підтримку, повертайтеся пізніше для більшої кількості реклам
+ Can we use your data to display ads?
+ You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details
+ Personalized ads
+ Non-personalized ads
Додатково
Вигляд та поведінка
Сповіщення
From 09e07a17136cc7266706dc6a8764619cb2ab8277 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Mon, 22 Aug 2022 17:58:35 +0200
Subject: [PATCH 194/669] Version 1.7.0
---
app/build.gradle | 10 +++++-----
app/src/main/play/release-notes/pl-PL/default.txt | 8 ++++++--
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index efa9b3e09..e99e8773f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,8 +23,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 32
- versionCode 108
- versionName "1.6.4"
+ versionCode 109
+ versionName "1.7.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -161,8 +161,8 @@ play {
defaultToAppBundles = false
track = 'production'
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
- userFraction = 0.50d
- updatePriority = 3
+ userFraction = 0.05d
+ updatePriority = 5
enabled.set(false)
}
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:dbe87aac"
+ implementation "io.github.wulkanowy:sdk:1.7.0"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.6'
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 b6340bb93..3eb42eb97 100644
--- a/app/src/main/play/release-notes/pl-PL/default.txt
+++ b/app/src/main/play/release-notes/pl-PL/default.txt
@@ -1,5 +1,9 @@
-Wersja 1.6.4
+Wersja 1.7.0
-- naprawiliśmy błąd ładowania frekwencji na GPE i Lubelskim Portalu Oświatowym
+- naprawiliśmy logowanie do aplikacji
+- dodaliśmy wsparcie nowego modułu Wiadomości Plus
+- dodaliśmy nową możliwość wsparcia naszego projektu przez opcjonalne reklamy
+- dodaliśmy sortowanie po średniej
+- naprawiliśmy też kilka usterek wpływających na komfort używania aplikacji
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
From de1bc4809fe93679e89b944e0feb9771e6d3a8cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Tue, 23 Aug 2022 00:08:39 +0200
Subject: [PATCH 195/669] Fix ads translations (#1951)
---
.github/workflows/deploy-store.yml | 1 +
app/src/main/res/layout/dialog_ads_consent.xml | 12 ++++++------
app/src/main/res/values/strings.xml | 7 +++++++
app/src/play/res/xml/scheme_preferences_ads.xml | 8 ++++----
4 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/.github/workflows/deploy-store.yml b/.github/workflows/deploy-store.yml
index cfb0fb523..3ce618ca7 100644
--- a/.github/workflows/deploy-store.yml
+++ b/.github/workflows/deploy-store.yml
@@ -38,6 +38,7 @@ jobs:
ANDROID_PUBLISHER_CREDENTIALS: ${{ secrets.ANDROID_PUBLISHER_CREDENTIALS }}
ADMOB_PROJECT_ID: ${{ secrets.ADMOB_PROJECT_ID }}
SINGLE_SUPPORT_AD_ID: ${{ secrets.SINGLE_SUPPORT_AD_ID }}
+ DASHBOARD_TILE_AD_ID: ${{ secrets.DASHBOARD_TILE_AD_ID }}
SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }}
run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace;
diff --git a/app/src/main/res/layout/dialog_ads_consent.xml b/app/src/main/res/layout/dialog_ads_consent.xml
index 395101627..816074783 100644
--- a/app/src/main/res/layout/dialog_ads_consent.xml
+++ b/app/src/main/res/layout/dialog_ads_consent.xml
@@ -15,7 +15,7 @@
android:insetRight="0dp"
android:insetBottom="0dp"
android:padding="0dp"
- android:text="Privacy Policy"
+ android:text="@string/pref_ads_privacy_policy"
android:textAllCaps="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -26,9 +26,9 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="17dp"
android:layout_marginTop="8dp"
- android:text="I am over 18 years old"
+ android:text="@string/pref_ads_over_18_years_old"
android:textColor="?android:textColorSecondary"
- android:textSize="14dp"
+ android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@id/ads_consent_privacy" />
@@ -73,7 +73,7 @@
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
- android:text="Cancel"
+ android:text="@android:string/cancel"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0" />
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2eee4cce4..bcd498036 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -714,6 +714,10 @@
Show arithmetic average when no weights provided
Support
+ Privacy Policy
+ Agreements
+ Consent to processing of data related to ads
+ Show ads in app
Watch single ad to support project
Consent to data processing
To view an advertisement you must agree to the data processing terms of our Privacy Policy
@@ -725,6 +729,9 @@
You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details
Personalized ads
Non-personalized ads
+ I am over 18 years old
+ Yes, personalized ads
+ Yes, non-personalized ads
Advanced
Appearance & Behavior
diff --git a/app/src/play/res/xml/scheme_preferences_ads.xml b/app/src/play/res/xml/scheme_preferences_ads.xml
index d5e93a355..4165561a7 100644
--- a/app/src/play/res/xml/scheme_preferences_ads.xml
+++ b/app/src/play/res/xml/scheme_preferences_ads.xml
@@ -2,18 +2,18 @@
+ app:title="@string/pref_ads_agreements">
+ app:title="@string/pref_ads_privacy_policy" />
+ app:title="@string/pref_ads_consent" />
+ app:title="@string/pref_ads_show_in_app" />
Date: Tue, 23 Aug 2022 00:54:19 +0200
Subject: [PATCH 196/669] New Crowdin updates (#1952)
---
app/src/main/res/values-cs/strings.xml | 7 +++++++
app/src/main/res/values-de/strings.xml | 7 +++++++
app/src/main/res/values-pl/strings.xml | 7 +++++++
app/src/main/res/values-ru/strings.xml | 7 +++++++
app/src/main/res/values-sk/strings.xml | 7 +++++++
app/src/main/res/values-uk/strings.xml | 7 +++++++
app/src/main/res/values/strings.xml | 4 ++--
7 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 5f76bb6e1..d036e7e42 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -720,6 +720,10 @@
Odpovědět s historií zpráv
Vypočítat aritmetický průměr, pokud žádná známka nemá váhu
Podpora
+ Ochrana osobních údajů
+ Souhlasy
+ Souhlas se zpracováním údajů souvisejících s reklamami
+ Zobrazit reklamy v aplikaci
Podívejte se na jednu reklamu pro podporu projektu
Souhlas se zpracováním dat
Jestli chcete sledovat reklamu, musíte souhlasit s podmínkami zpracování údajů v našich Zásadách Ochrany Osobních Údajů
@@ -731,6 +735,9 @@
Volbu můžete kdykoliv změnit v nastavení aplikace. Můžeme použít Vaše data k zobrazení reklam šitých pro vás nebo pomocí méně vašich dat zobrazovat nepřizpůsobené reklamy. Podrobnosti naleznete v našich Zásadách ochrany osobních údajů
Přizpůsobené reklamy
Nepřizpůsobené reklamy
+ Mám ukončené 18 let
+ Ano, přizpůsobené reklamy
+ Ano, nepřizpůsobené reklamy
Pokročilé
Vzhled a chování
Oznámení
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index ef79cee12..4dfeee4f6 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -632,6 +632,10 @@
Antwort mit Nachrichtenhistorie
Arithmetisches Mittel anzeigen, wenn keine Gewichte angegeben sind
Unterstützung
+ Privacy Policy
+ Agreements
+ Consent to processing of data related to ads
+ Show ads in app
Einzelanzeige ansehen, um Projekt zu unterstützen
Einwilligung in die Datenverarbeitung
Um eine Anzeige zu sehen, müssen Sie mit den Datenverarbeitungsbedingungen unserer Datenschutzerklärung einverstanden sein
@@ -643,6 +647,9 @@
Sie können Ihre Wahl jederzeit in den App-Einstellungen ändern. Wir verwenden Ihre Daten, um auf Sie zugeschnittene Anzeigen anzuzeigen oder unter Verwendung weniger Ihrer Daten nicht personalisierte Werbung anzuzeigen. Bitte lesen Sie unsere Datenschutzerklärung für Details
Personalisierte Werbung
keine personalisierte Werbung
+ I am over 18 years old
+ Yes, personalized ads
+ Yes, non-personalized ads
Erweitert
Aussehen & Verhalten
Benachrichtigungen
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 566519e8c..c9abbd022 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -720,6 +720,10 @@
Odpowiadaj z historią wiadomości
Licz średnią arytmetyczną, gdy żadna ocena nie ma wagi
Wsparcie
+ Polityka prywatności
+ Zgody
+ Zgoda na przetwarzanie danych związanych z reklamami
+ Pokazuj reklamy w aplikacji
Obejrzyj pojedynczą reklamę, aby wesprzeć projekt
Zgoda na przetwarzanie danych
Aby obejrzeć reklamę, musisz zaakceptować warunki przetwarzania danych zawarte w naszej Polityce Prywatności
@@ -731,6 +735,9 @@
Możesz zmienić swój wybór w dowolnym momencie w ustawieniach aplikacji. Możemy wykorzystać Twoje dane do wyświetlania reklam dostosowanych do Ciebie lub, przy użyciu mniejszej ilości danych, wyświetlić niepersonalizowane reklamy. Zobacz naszą Politykę Prywatności, aby uzyskać więcej informacji
Spersonalizowane reklamy
Niespersonalizowane reklamy
+ Mam ukończone 18 lat
+ Tak, spersonalizowane reklamy
+ Tak, niespersonalizowane reklamy
Zaawansowane
Wygląd i zachowanie
Powiadomienia
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index a3c5a62df..e1abb0293 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -720,6 +720,10 @@
Отвечать с историей сообщений
Показывать среднее арифметическое при отсутствии стоимости
Поддержка
+ Privacy Policy
+ Agreements
+ Consent to processing of data related to ads
+ Show ads in app
Посмотреть рекламу для поддержки проекта
Согласие на обработку данных
Для просмотра рекламы вы должны согласиться с условиями обработки данных нашей Политики конфиденциальности
@@ -731,6 +735,9 @@
You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details
Personalized ads
Non-personalized ads
+ I am over 18 years old
+ Yes, personalized ads
+ Yes, non-personalized ads
Расширенные
Внешний вид и поведение
Уведомления
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 6a4505d70..1c6eae8c0 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -720,6 +720,10 @@
Odpovedať s históriou správ
Vypočítať aritmetický priemer, ak žiadna známka nemá váhu
Podpora
+ Ochrana osobných údajov
+ Súhlasy
+ Súhlas so spracovaním údajov súvisiacich s reklamami
+ Zobraziť reklamy v aplikácii
Pozrite sa na jednu reklamu pre podporu projektu
Súhlas so spracovaním dát
Ak chcete sledovať reklamu, musíte súhlasiť s podmienkami spracovania údajov v našich Zásadách Ochrany Osobných Údajov
@@ -731,6 +735,9 @@
Voľbu môžete kedykoľvek zmeniť v nastavení aplikácie. Môžeme použiť vaše údaje na zobrazenie reklám šitých pre vás alebo pomocou menej vašich dát zobrazovať neprispôsobené reklamy. Podrobnosti nájdete v našich Zásadách ochrany osobných údajov
Prispôsobené reklamy
Neprispôsobené reklamy
+ Mám ukončené 18 rokov
+ Áno, prispôsobené reklamy
+ Áno, neprispôsobené reklamy
Pokročilé
Vzhľad a správanie
Oznámenia
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 742e800f3..f90e78e7d 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -720,6 +720,10 @@
Відповісти з історією повідомлень
Вилічити середню аритметичну, якщо оцінка немає вартості
Підтримка
+ Privacy Policy
+ Agreements
+ Consent to processing of data related to ads
+ Show ads in app
Подивіться одну рекламу для підтримки проєкту
Згода в обробці даних
Щоб переглянути рекламу, ви повинні погодитися з умовами обробки даних нашої Політики конфіденційності
@@ -731,6 +735,9 @@
You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details
Personalized ads
Non-personalized ads
+ I am over 18 years old
+ Yes, personalized ads
+ Yes, non-personalized ads
Додатково
Вигляд та поведінка
Сповіщення
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bcd498036..e4542547d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -714,8 +714,8 @@
Show arithmetic average when no weights provided
Support
- Privacy Policy
- Agreements
+ Privacy Policy
+ Agreements
Consent to processing of data related to ads
Show ads in app
Watch single ad to support project
From 10c36f19bf13234c758b2ac2e96d65a71df8e82c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Tue, 23 Aug 2022 00:56:12 +0200
Subject: [PATCH 197/669] Version 1.7.1
---
app/build.gradle | 6 +++---
app/src/main/play/release-notes/pl-PL/default.txt | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index e99e8773f..5fb07377a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,8 +23,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 32
- versionCode 109
- versionName "1.7.0"
+ versionCode 110
+ versionName "1.7.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -161,7 +161,7 @@ play {
defaultToAppBundles = false
track = 'production'
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
- userFraction = 0.05d
+ userFraction = 0.95d
updatePriority = 5
enabled.set(false)
}
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 3eb42eb97..4b6dcaf28 100644
--- a/app/src/main/play/release-notes/pl-PL/default.txt
+++ b/app/src/main/play/release-notes/pl-PL/default.txt
@@ -1,4 +1,4 @@
-Wersja 1.7.0
+Wersja 1.7.1
- naprawiliśmy logowanie do aplikacji
- dodaliśmy wsparcie nowego modułu Wiadomości Plus
From 70c2cb7dbf717c508bbec08b69931019b2430699 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 29 Aug 2022 13:30:00 +0000
Subject: [PATCH 198/669] Bump desugar_jdk_libs from 1.1.6 to 1.1.8 (#1957)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 5fb07377a..52ae02ddc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -188,7 +188,7 @@ ext {
dependencies {
implementation "io.github.wulkanowy:sdk:1.7.0"
- coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.6'
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
From f68a8e4215e719ecdfedeb72553ce58df1fcfbf7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 29 Aug 2022 13:30:28 +0000
Subject: [PATCH 199/669] Bump robolectric from 4.8.1 to 4.8.2 (#1956)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 52ae02ddc..1dba83c5d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -263,7 +263,7 @@ dependencies {
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
- testImplementation 'org.robolectric:robolectric:4.8.1'
+ testImplementation 'org.robolectric:robolectric:4.8.2'
testImplementation "androidx.test:runner:1.4.0"
testImplementation "androidx.test.ext:junit:1.1.3"
testImplementation "androidx.test:core:1.4.0"
From 5c17c38d1d16214db4cc423ffe8556e7b7c64d8b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 29 Aug 2022 13:30:56 +0000
Subject: [PATCH 200/669] Bump mockk from 1.12.5 to 1.12.7 (#1955)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 1dba83c5d..10648c15f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -181,7 +181,7 @@ ext {
android_hilt = "1.0.0"
room = "2.4.3"
chucker = "3.5.2"
- mockk = "1.12.5"
+ mockk = "1.12.7"
coroutines = "1.6.4"
}
From 54372e0a55e8e903f868d4e47897e9c1a868b505 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 29 Aug 2022 13:40:14 +0000
Subject: [PATCH 201/669] Bump kotlinx-serialization-json from 1.3.3 to 1.4.0
(#1950)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 10648c15f..5977b347b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -190,7 +190,7 @@ dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
- implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
implementation "androidx.core:core-ktx:1.8.0"
From f2cb3b4f9edd69a1936d3ccb52b3b8eab0e0e320 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Tue, 30 Aug 2022 09:06:29 +0200
Subject: [PATCH 202/669] Change message draft key in shared preferences
(#1959)
---
.../github/wulkanowy/data/repositories/MessageRepository.kt | 4 ++--
app/src/main/res/values/preferences_keys.xml | 3 +--
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
index 00cbffb84..5a43da4e2 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
@@ -167,10 +167,10 @@ class MessageRepository @Inject constructor(
}
var draftMessage: MessageDraft?
- get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft))
+ get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_draft))
?.let { json.decodeFromString(it) }
set(value) = sharedPrefProvider.putString(
- context.getString(R.string.pref_key_message_send_draft),
+ context.getString(R.string.pref_key_message_draft),
value?.let { json.encodeToString(it) }
)
}
diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml
index f29080b33..80a71bc71 100644
--- a/app/src/main/res/values/preferences_keys.xml
+++ b/app/src/main/res/values/preferences_keys.xml
@@ -31,8 +31,7 @@
homework_fullscreen
subjects_without_grades
optional_arithmetic_average
- message_send_is_draft
- message_send_recipients
+ message_draft
last_sync_date
notifications_piggyback
notifications_piggyback_cancel_original
From 535206056d9b385a5cea1e15a53f0a1c7f37cb4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Tue, 30 Aug 2022 09:07:07 +0200
Subject: [PATCH 203/669] Fix selecting student mailbox based on studentName
field (#1958)
---
app/build.gradle | 2 +-
.../wulkanowy/data/db/dao/MailboxDao.kt | 3 -
.../data/repositories/MailboxRepository.kt | 11 +-
.../repositories/MailboxRepositoryTest.kt | 132 ++++++++++++++++++
4 files changed, 141 insertions(+), 7 deletions(-)
create mode 100644 app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt
diff --git a/app/build.gradle b/app/build.gradle
index 5977b347b..62eb8e315 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:1.7.0"
+ implementation "io.github.wulkanowy:sdk:09e5215894"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt
index 8589db311..c44ecd0c2 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt
@@ -11,7 +11,4 @@ interface MailboxDao : BaseDao {
@Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId ")
suspend fun loadAll(userLoginId: Int): List
-
- @Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId AND studentName = :studentName ")
- suspend fun load(userLoginId: Int, studentName: String): Mailbox?
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
index 7f5974920..8c1097bdb 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
@@ -32,17 +32,22 @@ class MailboxRepository @Inject constructor(
suspend fun getMailbox(student: Student): Mailbox {
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
- val mailbox = mailboxDao.load(student.userLoginId, student.studentName)
+ val mailboxes = mailboxDao.loadAll(student.userLoginId)
+ val mailbox = mailboxes.filterByStudent(student)
return if (isExpired || mailbox == null) {
refreshMailboxes(student)
- val newMailbox = mailboxDao.load(student.userLoginId, student.studentName)
+ val newMailbox = mailboxDao.loadAll(student.userLoginId).filterByStudent(student)
requireNotNull(newMailbox) {
- "Mailbox for ${student.userName} - ${student.studentName} not found!"
+ "Mailbox for ${student.userName} - ${student.studentName} not found! Saved mailboxes: $mailboxes"
}
newMailbox
} else mailbox
}
+
+ private fun List.filterByStudent(student: Student): Mailbox? = find {
+ it.studentName.trim() == student.studentName.trim()
+ }
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt
new file mode 100644
index 000000000..56e315638
--- /dev/null
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt
@@ -0,0 +1,132 @@
+package io.github.wulkanowy.data.repositories
+
+import io.github.wulkanowy.data.db.dao.MailboxDao
+import io.github.wulkanowy.data.db.entities.Mailbox
+import io.github.wulkanowy.data.db.entities.MailboxType
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.sdk.Sdk
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.impl.annotations.MockK
+import io.mockk.impl.annotations.SpyK
+import io.mockk.just
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import java.time.Instant
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class MailboxRepositoryTest {
+
+ @SpyK
+ private var sdk = Sdk()
+
+ @MockK
+ private lateinit var mailboxDao: MailboxDao
+
+ @MockK
+ private lateinit var refreshHelper: AutoRefreshHelper
+
+ private lateinit var systemUnderTest: MailboxRepository
+
+ @Before
+ fun setUp() {
+ MockKAnnotations.init(this)
+
+ coEvery { refreshHelper.shouldBeRefreshed(any()) } returns false
+ coEvery { refreshHelper.updateLastRefreshTimestamp(any()) } just Runs
+ coEvery { mailboxDao.deleteAll(any()) } just Runs
+ coEvery { mailboxDao.insertAll(any()) } returns emptyList()
+ coEvery { mailboxDao.loadAll(any()) } returns emptyList()
+ coEvery { sdk.getMailboxes() } returns emptyList()
+
+ systemUnderTest = MailboxRepository(
+ mailboxDao = mailboxDao,
+ sdk = sdk,
+ refreshHelper = refreshHelper,
+ )
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `get mailbox that doesn't exist`() = runTest {
+ val student = getStudentEntity(
+ userName = "Stanisław Kowalski",
+ studentName = "Jan Kowalski",
+ )
+ coEvery { sdk.getMailboxes() } returns emptyList()
+
+ systemUnderTest.getMailbox(student)
+ }
+
+ @Test
+ fun `get mailbox for user with additional spaces`() = runTest {
+ val student = getStudentEntity(
+ userName = " Stanisław Kowalski ",
+ studentName = " Jan Kowalski ",
+ )
+ val expectedMailbox = getMailboxEntity("Jan Kowalski ")
+ coEvery { mailboxDao.loadAll(any()) } returns listOf(
+ expectedMailbox,
+ )
+
+ val selectedMailbox = systemUnderTest.getMailbox(student)
+ assertEquals(expectedMailbox, selectedMailbox)
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `get mailbox for student with strange name`() = runTest {
+ val student = getStudentEntity(
+ userName = "Stanisław Kowalski",
+ studentName = "J**** K*****",
+ )
+ val expectedMailbox = getMailboxEntity("Jan Kowalski")
+ coEvery { mailboxDao.loadAll(any()) } returns listOf(
+ expectedMailbox,
+ )
+
+ systemUnderTest.getMailbox(student)
+ }
+
+ private fun getMailboxEntity(
+ studentName: String,
+ ) = Mailbox(
+ globalKey = "",
+ fullName = "",
+ userName = "",
+ userLoginId = 123,
+ studentName = studentName,
+ schoolNameShort = "",
+ type = MailboxType.STUDENT,
+ )
+
+ private fun getStudentEntity(
+ studentName: String,
+ userName: String,
+ ) = Student(
+ scrapperBaseUrl = "http://fakelog.cf",
+ email = "jan@fakelog.cf",
+ certificateKey = "",
+ classId = 0,
+ className = "",
+ isCurrent = false,
+ isParent = false,
+ loginMode = Sdk.Mode.API.name,
+ loginType = Sdk.ScrapperLoginType.STANDARD.name,
+ mobileBaseUrl = "",
+ password = "",
+ privateKey = "",
+ registrationDate = Instant.now(),
+ schoolName = "",
+ schoolShortName = "test",
+ schoolSymbol = "",
+ studentId = 1,
+ studentName = studentName,
+ symbol = "",
+ userLoginId = 1,
+ userName = userName,
+ )
+}
From eed091aad29b76ac3340e84ae90f4ffc01868491 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Tue, 30 Aug 2022 09:07:24 +0200
Subject: [PATCH 204/669] Update row in mailboxes table on primary key conflict
(#1954)
---
app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt
index 048e9e3cd..056a5cbd1 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt
@@ -2,11 +2,12 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Delete
import androidx.room.Insert
+import androidx.room.OnConflictStrategy
import androidx.room.Update
interface BaseDao {
- @Insert
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(items: List): List
@Update
From bf34cb0c1e483b0253e12359c97e332404385e3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Tue, 30 Aug 2022 12:11:32 +0200
Subject: [PATCH 205/669] New Crowdin updates (#1953)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Mikołaj Pich
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 62eb8e315..b70e77b73 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:09e5215894"
+ implementation "io.github.wulkanowy:sdk:a1bf69486b"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
From d139bd5b14a3b068f217f4ec107b5484f5539856 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Tue, 30 Aug 2022 13:34:10 +0200
Subject: [PATCH 206/669] Version 1.7.2
---
app/build.gradle | 8 ++++----
app/src/main/play/release-notes/pl-PL/default.txt | 8 ++------
2 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index b70e77b73..759199cad 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,8 +23,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 32
- versionCode 110
- versionName "1.7.1"
+ versionCode 111
+ versionName "1.7.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -161,7 +161,7 @@ play {
defaultToAppBundles = false
track = 'production'
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
- userFraction = 0.95d
+ userFraction = 0.05d
updatePriority = 5
enabled.set(false)
}
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:a1bf69486b"
+ implementation "io.github.wulkanowy:sdk:1.7.2"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
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 4b6dcaf28..69699208a 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,5 @@
-Wersja 1.7.1
+Wersja 1.7.2
-- naprawiliśmy logowanie do aplikacji
-- dodaliśmy wsparcie nowego modułu Wiadomości Plus
-- dodaliśmy nową możliwość wsparcia naszego projektu przez opcjonalne reklamy
-- dodaliśmy sortowanie po średniej
-- naprawiliśmy też kilka usterek wpływających na komfort używania aplikacji
+- naprawiliśmy kilka błędów w obsłudze nowego modułu wiadomości
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
From 558db061f594b709e10a9766ae3cdfccbf8dbed1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Wed, 31 Aug 2022 18:31:12 +0200
Subject: [PATCH 207/669] Fix marking message as read (#1960)
* Fix marking message as read
* Update sdk
* Update sdk
* Fix tests
* Use many recipients strings instead of first recipient
---
app/build.gradle | 2 +-
.../wulkanowy/data/repositories/MessageRepository.kt | 9 ++++++---
.../wulkanowy/data/repositories/MessageRepositoryTest.kt | 2 +-
3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 759199cad..ab8add505 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:1.7.2"
+ implementation "com.github.wulkanowy:sdk:006a238bda"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
index 5a43da4e2..e7428762e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
@@ -64,7 +64,10 @@ class MessageRepository @Inject constructor(
},
query = { messagesDb.loadAll(mailbox.globalKey, folder.id) },
fetch = {
- sdk.init(student).getMessages(Folder.valueOf(folder.name)).mapToEntities(mailbox)
+ sdk.init(student).getMessages(
+ folder = Folder.valueOf(folder.name),
+ mailboxKey = mailbox.globalKey,
+ ).mapToEntities(mailbox)
},
saveFetchResult = { old, new ->
messagesDb.deleteAll(old uniqueSubtract new)
@@ -89,7 +92,7 @@ class MessageRepository @Inject constructor(
},
query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) },
fetch = {
- sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey)
+ sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey, markAsRead)
},
saveFetchResult = { old, new ->
checkNotNull(old) { "Fetched message no longer exist!" }
@@ -98,7 +101,7 @@ class MessageRepository @Inject constructor(
id = message.id
unread = !markAsRead
sender = new.sender
- recipients = new.recipients.firstOrNull() ?: "Wielu adresoatów"
+ recipients = new.recipients.singleOrNull() ?: "Wielu adresatów"
content = content.ifBlank { new.content }
})
)
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
index 24306bfeb..4efc9c60a 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
@@ -141,7 +141,7 @@ class MessageRepositoryTest {
messageDb.loadMessageWithAttachment("v4")
} returnsMany listOf(flowOf(mWa), flowOf(mWaWithContent))
coEvery {
- sdk.getMessageDetails("v4")
+ sdk.getMessageDetails("v4", any())
} returns mockk {
every { sender } returns ""
every { recipients } returns listOf("")
From d566de0282eb5ac77688233aff677673c6ec15ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Wed, 31 Aug 2022 19:31:28 +0200
Subject: [PATCH 208/669] Version 1.7.3
---
app/build.gradle | 10 +++++-----
app/src/main/play/release-notes/pl-PL/default.txt | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index ab8add505..d4a9b9021 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,8 +23,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 32
- versionCode 111
- versionName "1.7.2"
+ versionCode 112
+ versionName "1.7.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -160,8 +160,8 @@ kapt {
play {
defaultToAppBundles = false
track = 'production'
- releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
- userFraction = 0.05d
+// releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
+// userFraction = 0.05d
updatePriority = 5
enabled.set(false)
}
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "com.github.wulkanowy:sdk:006a238bda"
+ implementation "io.github.wulkanowy:sdk:1.7.3"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
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 69699208a..ac19bc99a 100644
--- a/app/src/main/play/release-notes/pl-PL/default.txt
+++ b/app/src/main/play/release-notes/pl-PL/default.txt
@@ -1,4 +1,4 @@
-Wersja 1.7.2
+Wersja 1.7.3
- naprawiliśmy kilka błędów w obsłudze nowego modułu wiadomości
From 6153c7b97d58f36383f05d4357194c9c3dcb7720 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Thu, 1 Sep 2022 14:55:00 +0200
Subject: [PATCH 209/669] Add support for match mailboxes with more different
names (#1961)
---
.../data/repositories/MailboxRepository.kt | 34 +++++++++-
.../repositories/MailboxRepositoryTest.kt | 62 ++++++++++++++++++-
2 files changed, 91 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
index 8c1097bdb..ad4f669e2 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
@@ -47,7 +47,37 @@ class MailboxRepository @Inject constructor(
} else mailbox
}
- private fun List.filterByStudent(student: Student): Mailbox? = find {
- it.studentName.trim() == student.studentName.trim()
+ private fun List.filterByStudent(student: Student): Mailbox? {
+ val normalizedStudentName = student.studentName.normalizeStudentName()
+
+ return find {
+ it.studentName.normalizeStudentName() == normalizedStudentName
+ } ?: singleOrNull {
+ it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart()
+ } ?: singleOrNull {
+ it.studentName.getUnauthorizedVersion() == normalizedStudentName
+ }
+ }
+
+ private fun String.normalizeStudentName(): String {
+ return trim().split(" ").joinToString(" ") { part ->
+ part.lowercase().replaceFirstChar { it.uppercase() }
+ }
+ }
+
+ private fun String.getFirstAndLastPart(): String {
+ val parts = normalizeStudentName().split(" ")
+
+ val endParts = parts.filterIndexed { i, _ ->
+ i == 0 || parts.size == i - 1
+ }
+ return endParts.joinToString(" ")
+ }
+
+ private fun String.getUnauthorizedVersion(): String {
+ return normalizeStudentName().split(" ")
+ .joinToString(" ") {
+ it.first() + "*".repeat(it.length - 1)
+ }
}
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt
index 56e315638..300662f01 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt
@@ -77,20 +77,76 @@ class MailboxRepositoryTest {
assertEquals(expectedMailbox, selectedMailbox)
}
- @Test(expected = IllegalArgumentException::class)
- fun `get mailbox for student with strange name`() = runTest {
+ @Test
+ fun `get mailbox for unique non-authorized student`() = runTest {
val student = getStudentEntity(
userName = "Stanisław Kowalski",
- studentName = "J**** K*****",
+ studentName = "J** K*******",
)
val expectedMailbox = getMailboxEntity("Jan Kowalski")
coEvery { mailboxDao.loadAll(any()) } returns listOf(
expectedMailbox,
)
+ assertEquals(expectedMailbox, systemUnderTest.getMailbox(student))
+ }
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `get mailbox for not-unique non-authorized student`() = runTest {
+ val student = getStudentEntity(
+ userName = "Stanisław Kowalski",
+ studentName = "J** K*******",
+ )
+ coEvery { mailboxDao.loadAll(any()) } returns listOf(
+ getMailboxEntity("Jan Kowalski"),
+ getMailboxEntity("Jan Kurowski"),
+ )
+
systemUnderTest.getMailbox(student)
}
+ @Test
+ fun `get mailbox for student with uppercase name`() = runTest {
+ val student = getStudentEntity(
+ userName = "Mochoń Julia",
+ studentName = "KLAUDIA MOCHOŃ",
+ )
+ val expectedMailbox = getMailboxEntity("Klaudia Mochoń")
+ coEvery { mailboxDao.loadAll(any()) } returns listOf(
+ expectedMailbox,
+ )
+
+ assertEquals(expectedMailbox, systemUnderTest.getMailbox(student))
+ }
+
+ @Test
+ fun `get mailbox for student with second name`() = runTest {
+ val student = getStudentEntity(
+ userName = "Fistaszek Karolina",
+ studentName = "Julia Fistaszek",
+ )
+ val expectedMailbox = getMailboxEntity("Julia Maria Fistaszek")
+ coEvery { mailboxDao.loadAll(any()) } returns listOf(
+ expectedMailbox,
+ )
+
+ assertEquals(expectedMailbox, systemUnderTest.getMailbox(student))
+ }
+
+ @Test
+ fun `get mailbox for student with second name and uppercase`() = runTest {
+ val student = getStudentEntity(
+ userName = "BEDNAREK KAMIL",
+ studentName = "ALEKSANDRA BEDNAREK",
+ )
+ val expectedMailbox = getMailboxEntity("Aleksandra Anna Bednarek")
+ coEvery { mailboxDao.loadAll(any()) } returns listOf(
+ expectedMailbox,
+ )
+
+ assertEquals(expectedMailbox, systemUnderTest.getMailbox(student))
+ }
+
private fun getMailboxEntity(
studentName: String,
) = Mailbox(
From e05abb3539c1d0d46d48f0c9528df2009a714723 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Thu, 1 Sep 2022 17:48:03 +0200
Subject: [PATCH 210/669] Fix showing empty view in grade details when there is
no grades (#1963)
---
.../ui/modules/grade/details/GradeDetailsPresenter.kt | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
index 8cde5d6b9..4261c507d 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt
@@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.grade.details
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.enums.GradeExpandMode
-import io.github.wulkanowy.data.enums.GradeSortingMode
import io.github.wulkanowy.data.enums.GradeSortingMode.*
import io.github.wulkanowy.data.repositories.GradeRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
@@ -132,16 +131,17 @@ class GradeDetailsPresenter @Inject constructor(
}
.logResourceStatus("load grade details")
.onResourceData {
+ val gradeItems = createGradeItems(it)
view?.run {
enableSwipe(true)
showProgress(false)
showErrorView(false)
- showContent(it.isNotEmpty())
- showEmpty(it.isEmpty())
+ showContent(gradeItems.isNotEmpty())
+ showEmpty(gradeItems.isEmpty())
updateNewGradesAmount(it)
updateMarkAsDoneButton()
updateData(
- data = createGradeItems(it),
+ data = gradeItems,
expandMode = preferencesRepository.gradeExpandMode,
preferencesRepository.gradeColorTheme
)
From bc22808b0ea2ece030ff3e9ef24d5af231e22b93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Thu, 1 Sep 2022 17:48:46 +0200
Subject: [PATCH 211/669] Add support for matching last kingergarten semester
if there is no any current (#1962)
---
.../wulkanowy/utils/SemesterExtension.kt | 3 +
.../utils/SemesterExtensionKtTest.kt | 72 +++++++++++++++++++
2 files changed, 75 insertions(+)
create mode 100644 app/src/test/java/io/github/wulkanowy/utils/SemesterExtensionKtTest.kt
diff --git a/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt
index 6e11a8b2c..380d6bf6e 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/SemesterExtension.kt
@@ -15,5 +15,8 @@ fun List.getCurrentOrLast(): Semester {
// when there is more than one current semester - find one with higher id
singleOrNull { semester -> semester.semesterId == maxByOrNull { it.semesterId }?.semesterId }?.let { return it }
+ // when there is no active kindergarten semester - get one from last year
+ singleOrNull { semester -> semester.schoolYear == maxByOrNull { it.schoolYear }?.schoolYear }?.let { return it }
+
throw IllegalArgumentException("Duplicated last semester! Semesters: ${joinToString(separator = "\n")}")
}
diff --git a/app/src/test/java/io/github/wulkanowy/utils/SemesterExtensionKtTest.kt b/app/src/test/java/io/github/wulkanowy/utils/SemesterExtensionKtTest.kt
new file mode 100644
index 000000000..b7d3ecc94
--- /dev/null
+++ b/app/src/test/java/io/github/wulkanowy/utils/SemesterExtensionKtTest.kt
@@ -0,0 +1,72 @@
+package io.github.wulkanowy.utils
+
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.getSemesterEntity
+import org.junit.Test
+import java.time.LocalDate
+import kotlin.test.assertEquals
+
+class SemesterExtensionKtTest {
+
+ @Test(expected = IllegalArgumentException::class)
+ fun `get current semester when current is doubled`() {
+ val semesters = listOf(
+ getSemesterEntity(1, 1, LocalDate.now(), LocalDate.now()),
+ getSemesterEntity(1, 1, LocalDate.now(), LocalDate.now())
+ )
+
+ semesters.getCurrentOrLast()
+ }
+
+ @Test(expected = RuntimeException::class)
+ fun `get current semester when there is empty list`() {
+ val semesters = listOf()
+
+ semesters.getCurrentOrLast()
+ }
+
+ @Test
+ fun `get current kindergarten semester when there is no any current`() {
+ val semesters = listOf(
+ createSemesterEntity(
+ kindergartenDiaryId = 281,
+ schoolYear = 2020,
+ semesterId = 0,
+ start = LocalDate.of(2020, 9, 1),
+ end = LocalDate.of(2021, 8, 31),
+ ),
+ createSemesterEntity(
+ kindergartenDiaryId = 342,
+ schoolYear = 2021,
+ semesterId = 0,
+ start = LocalDate.of(2021, 9, 1),
+ end = LocalDate.of(2022, 8, 31),
+ ),
+ )
+
+ val res = semesters.getCurrentOrLast()
+
+ assertEquals(2021, res.schoolYear)
+ }
+
+ private fun createSemesterEntity(
+ diaryId: Int = 0,
+ kindergartenDiaryId: Int = 0,
+ semesterId: Int = 0,
+ schoolYear: Int = 0,
+ start: LocalDate = LocalDate.now(),
+ end: LocalDate = LocalDate.now().plusMonths(6),
+ ) = Semester(
+ studentId = 1,
+ diaryId = diaryId,
+ kindergartenDiaryId = kindergartenDiaryId,
+ semesterId = semesterId,
+ diaryName = "$semesterId",
+ schoolYear = schoolYear,
+ classId = 0,
+ semesterName = semesterId,
+ unitId = 1,
+ start = start,
+ end = end
+ )
+}
From e67066f3aea1f32c1470b7e43c90cb3da013027e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Thu, 1 Sep 2022 17:57:20 +0200
Subject: [PATCH 212/669] Version 1.7.4
---
app/build.gradle | 6 +++---
app/src/main/play/release-notes/pl-PL/default.txt | 3 ++-
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index d4a9b9021..e8902fddd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,8 +23,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 32
- versionCode 112
- versionName "1.7.3"
+ versionCode 113
+ versionName "1.7.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:1.7.3"
+ implementation "io.github.wulkanowy:sdk:1.7.4"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
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 ac19bc99a..7fe3b7099 100644
--- a/app/src/main/play/release-notes/pl-PL/default.txt
+++ b/app/src/main/play/release-notes/pl-PL/default.txt
@@ -1,5 +1,6 @@
-Wersja 1.7.3
+Wersja 1.7.4
- naprawiliśmy kilka błędów w obsłudze nowego modułu wiadomości
+- naprawiliśmy wyświetlanie napisu "Brak ocen", jesli uczeń nie zdobył w danym semestrze jeszcze żadnych ocen
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
From 157becb017f3502e54ff42ba4f0dbc1bbcd136c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 2 Sep 2022 20:19:19 +0200
Subject: [PATCH 213/669] Fix matching mailboxes when there is more than one
space between words (#1964)
---
app/build.gradle | 2 +-
.../data/repositories/MailboxRepository.kt | 10 ++++++----
.../data/repositories/MailboxRepositoryTest.kt | 14 ++++++++++++++
3 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index e8902fddd..c9425d58f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:1.7.4"
+ implementation "io.github.wulkanowy:sdk:dcd8bd8b19"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
index ad4f669e2..c571937ad 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
@@ -60,16 +60,18 @@ class MailboxRepository @Inject constructor(
}
private fun String.normalizeStudentName(): String {
- return trim().split(" ").joinToString(" ") { part ->
- part.lowercase().replaceFirstChar { it.uppercase() }
- }
+ return trim().split(" ")
+ .filter { it.isNotBlank() }
+ .joinToString(" ") { part ->
+ part.lowercase().replaceFirstChar { it.uppercase() }
+ }
}
private fun String.getFirstAndLastPart(): String {
val parts = normalizeStudentName().split(" ")
val endParts = parts.filterIndexed { i, _ ->
- i == 0 || parts.size == i - 1
+ i == 0 || parts.size - 1 == i
}
return endParts.joinToString(" ")
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt
index 300662f01..9198560fe 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt
@@ -91,6 +91,20 @@ class MailboxRepositoryTest {
assertEquals(expectedMailbox, systemUnderTest.getMailbox(student))
}
+ @Test
+ fun `get mailbox for unique non-authorized student but with spaces`() = runTest {
+ val student = getStudentEntity(
+ userName = "Stanisław Kowalski",
+ studentName = "J** K*******",
+ )
+ val expectedMailbox = getMailboxEntity("Jan Kowalski")
+ coEvery { mailboxDao.loadAll(any()) } returns listOf(
+ expectedMailbox,
+ )
+
+ assertEquals(expectedMailbox, systemUnderTest.getMailbox(student))
+ }
+
@Test(expected = IllegalArgumentException::class)
fun `get mailbox for not-unique non-authorized student`() = runTest {
val student = getStudentEntity(
From 86f8763e697b2976d3b73f9d9f78dc102789191d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 2 Sep 2022 21:30:30 +0200
Subject: [PATCH 214/669] Display lesson number in attendance notification if
subject is blank (#1965)
---
.../services/sync/notifications/NewAttendanceNotification.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt
index 49842c9a6..99473a8ec 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewAttendanceNotification.kt
@@ -8,7 +8,6 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.GroupNotificationData
import io.github.wulkanowy.data.pojos.NotificationData
import io.github.wulkanowy.ui.modules.Destination
-import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.descriptionRes
import io.github.wulkanowy.utils.getPlural
import io.github.wulkanowy.utils.toFormattedString
@@ -22,8 +21,9 @@ class NewAttendanceNotification @Inject constructor(
suspend fun notify(items: List, student: Student) {
val lines = items.filterNot { it.presence || it.name == "UNKNOWN" }
.map {
+ val lesson = it.subject.ifBlank { "Lekcja ${it.number}" }
val description = context.getString(it.descriptionRes)
- "${it.date.toFormattedString("dd.MM")} - ${it.subject}: $description"
+ "${it.date.toFormattedString("dd.MM")} - $lesson: $description"
}
.ifEmpty { return }
From 59f6f5c2120cfc5ea02bc7e3f1a7d85e6197be51 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 2 Sep 2022 21:31:27 +0200
Subject: [PATCH 215/669] Version 1.7.5
---
app/build.gradle | 8 ++++----
app/src/main/play/release-notes/pl-PL/default.txt | 3 ++-
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index c9425d58f..6584b0d28 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,8 +23,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 32
- versionCode 113
- versionName "1.7.4"
+ versionCode 114
+ versionName "1.7.5"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -162,7 +162,7 @@ play {
track = 'production'
// releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
// userFraction = 0.05d
- updatePriority = 5
+ updatePriority = 4
enabled.set(false)
}
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:dcd8bd8b19"
+ implementation "io.github.wulkanowy:sdk:1.7.5"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
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 7fe3b7099..064401abd 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,7 @@
-Wersja 1.7.4
+Wersja 1.7.5
- naprawiliśmy kilka błędów w obsłudze nowego modułu wiadomości
- naprawiliśmy wyświetlanie napisu "Brak ocen", jesli uczeń nie zdobył w danym semestrze jeszcze żadnych ocen
+- naprawiliśmy logowanie do aplikacji rodzicom będącym jednocześnie nauczycielami
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
From 73a7255d3a003e15de313387bcbf40c73fa17ae5 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Sep 2022 19:49:30 +0000
Subject: [PATCH 216/669] Bump firebase-bom from 30.3.2 to 30.4.0 (#1968)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 6584b0d28..17a9fe42f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
- playImplementation platform('com.google.firebase:firebase-bom:30.3.2')
+ playImplementation platform('com.google.firebase:firebase-bom:30.4.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 46c29c438eb1a52824dff8d9b0d1d385c3366013 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 14 Sep 2022 11:17:19 +0000
Subject: [PATCH 217/669] Bump appcompat from 1.5.0 to 1.5.1 (#1975)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 17a9fe42f..41ead9f3e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -196,7 +196,7 @@ dependencies {
implementation "androidx.core:core-ktx:1.8.0"
implementation 'androidx.core:core-splashscreen:1.0.0'
implementation "androidx.activity:activity-ktx:1.5.1"
- implementation "androidx.appcompat:appcompat:1.5.0"
+ implementation "androidx.appcompat:appcompat:1.5.1"
implementation "androidx.fragment:fragment-ktx:1.5.2"
implementation "androidx.annotation:annotation:1.4.0"
From d3f869c6c2400a5b7fa427a45d89df0add4f5420 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 14 Sep 2022 11:18:09 +0000
Subject: [PATCH 218/669] Bump firebase-bom from 30.4.0 to 30.4.1 (#1971)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 41ead9f3e..c2dba71fb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
- playImplementation platform('com.google.firebase:firebase-bom:30.4.0')
+ playImplementation platform('com.google.firebase:firebase-bom:30.4.1')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From d5d45ed1baa7ec0c52abb0fba9e8bec67c6123e8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 14 Sep 2022 11:18:29 +0000
Subject: [PATCH 219/669] Bump play-services-ads from 21.1.0 to 21.2.0 (#1972)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index c2dba71fb..7bd4ed15f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -247,7 +247,7 @@ dependencies {
playImplementation 'com.google.firebase:firebase-crashlytics:'
playImplementation 'com.google.android.play:core:1.10.3'
playImplementation 'com.google.android.play:core-ktx:1.8.1'
- playImplementation 'com.google.android.gms:play-services-ads:21.1.0'
+ playImplementation 'com.google.android.gms:play-services-ads:21.2.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.7.0.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.1.300'
From a5c636853a9a15563caedf0f5bc90a3a9021d6fa Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 14 Sep 2022 11:19:05 +0000
Subject: [PATCH 220/669] Bump coil from 2.2.0 to 2.2.1 (#1973)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 7bd4ed15f..7b7251f89 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -236,7 +236,7 @@ dependencies {
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
- implementation "io.coil-kt:coil:2.2.0"
+ implementation "io.coil-kt:coil:2.2.1"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
From afbfb9761fe61339f364f88ef8fde9d4703cb4e4 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 21 Sep 2022 20:38:55 +0000
Subject: [PATCH 221/669] Bump mockk from 1.12.7 to 1.12.8 (#1986)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 7b7251f89..67b4fd07d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -181,7 +181,7 @@ ext {
android_hilt = "1.0.0"
room = "2.4.3"
chucker = "3.5.2"
- mockk = "1.12.7"
+ mockk = "1.12.8"
coroutines = "1.6.4"
}
From 3625c5c5187786f781ea446433f09f13b809861d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 21 Sep 2022 20:39:08 +0000
Subject: [PATCH 222/669] Bump firebase-bom from 30.4.1 to 30.5.0 (#1991)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 67b4fd07d..6f3f1e989 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
- playImplementation platform('com.google.firebase:firebase-bom:30.4.1')
+ playImplementation platform('com.google.firebase:firebase-bom:30.5.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 4f0519552e3dbce9e5cdecd10685ed1e85aca540 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 21 Sep 2022 20:39:29 +0000
Subject: [PATCH 223/669] Bump firebase-crashlytics-gradle from 2.9.1 to 2.9.2
(#1988)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 98c9dfb84..d98f6f27e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,7 +17,7 @@ buildscript {
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.13'
classpath 'com.huawei.agconnect:agcp:1.7.1.300'
- classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1'
+ classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513"
From f1db993feedb35e746f309dfa0a11c4d0092a0c0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 21 Sep 2022 20:40:36 +0000
Subject: [PATCH 224/669] Bump agconnect-crash from 1.7.1.300 to 1.7.2.300
(#1990)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 6f3f1e989..6fddc1a73 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -250,7 +250,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:21.2.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.7.0.300'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.1.300'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.2.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From a1dc00af42c73255eeec65704df526c3c877aac4 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 21 Sep 2022 20:49:36 +0000
Subject: [PATCH 225/669] Bump agcp from 1.7.1.300 to 1.7.2.300 (#1989)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index d98f6f27e..942cf4b66 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.2.2'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.13'
- classpath 'com.huawei.agconnect:agcp:1.7.1.300'
+ classpath 'com.huawei.agconnect:agcp:1.7.2.300'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4"
From 5148ff291b7b5c7b2c33061e7e769325c9728aee Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 21 Sep 2022 20:59:41 +0000
Subject: [PATCH 226/669] Bump google-services from 4.3.13 to 4.3.14 (#1992)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 942cf4b66..53fcfda5e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -15,7 +15,7 @@ buildscript {
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath 'com.android.tools.build:gradle:7.2.2'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
- classpath 'com.google.gms:google-services:4.3.13'
+ classpath 'com.google.gms:google-services:4.3.14'
classpath 'com.huawei.agconnect:agcp:1.7.2.300'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
From 1bbd249275b97b145011fdd30355664984c03bcb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 28 Sep 2022 20:25:58 +0000
Subject: [PATCH 227/669] Bump fragment-ktx from 1.5.2 to 1.5.3 (#1997)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 6fddc1a73..e67747d73 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -197,7 +197,7 @@ dependencies {
implementation 'androidx.core:core-splashscreen:1.0.0'
implementation "androidx.activity:activity-ktx:1.5.1"
implementation "androidx.appcompat:appcompat:1.5.1"
- implementation "androidx.fragment:fragment-ktx:1.5.2"
+ implementation "androidx.fragment:fragment-ktx:1.5.3"
implementation "androidx.annotation:annotation:1.4.0"
implementation "androidx.preference:preference-ktx:1.2.0"
From edbe45332ac522a6d3e78ad76a9d3af09edf5960 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 28 Sep 2022 20:27:43 +0000
Subject: [PATCH 228/669] Bump mockk from 1.12.8 to 1.13.1 (#1996)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index e67747d73..eaa81d233 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -181,7 +181,7 @@ ext {
android_hilt = "1.0.0"
room = "2.4.3"
chucker = "3.5.2"
- mockk = "1.12.8"
+ mockk = "1.13.1"
coroutines = "1.6.4"
}
From 8ca41b5ba348483bf9c10a2af927eb387526748e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 28 Sep 2022 20:28:01 +0000
Subject: [PATCH 229/669] Bump hianalytics from 6.7.0.300 to 6.8.0.300 (#1995)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index eaa81d233..8d33323f3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -249,7 +249,7 @@ dependencies {
playImplementation 'com.google.android.play:core-ktx:1.8.1'
playImplementation 'com.google.android.gms:play-services-ads:21.2.0'
- hmsImplementation 'com.huawei.hms:hianalytics:6.7.0.300'
+ hmsImplementation 'com.huawei.hms:hianalytics:6.8.0.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.2.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From b271c12ebccd7bb6e8ddaf18f68ed09071f385d9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 28 Sep 2022 20:28:31 +0000
Subject: [PATCH 230/669] Bump hilt_version from 2.43.2 to 2.44 (#1994)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 53fcfda5e..e308c579b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ buildscript {
ext {
kotlin_version = '1.7.10'
about_libraries = '10.4.0'
- hilt_version = "2.43.2"
+ hilt_version = "2.44"
}
repositories {
mavenCentral()
From 354f51dd707807c7c423b275df7ea4e672689443 Mon Sep 17 00:00:00 2001
From: Michael <5672750+mibac138@users.noreply.github.com>
Date: Wed, 28 Sep 2022 23:33:05 +0200
Subject: [PATCH 231/669] Fix student average calculation error in grade
statistics (#1981)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Mikołaj Pich
---
app/build.gradle | 2 +-
.../grade/summary/GradeSummaryAdapter.kt | 3 ++-
.../github/wulkanowy/utils/GradeExtension.kt | 24 ++++++++-----------
3 files changed, 13 insertions(+), 16 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 8d33323f3..1b745f0b6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:1.7.5"
+ implementation "io.github.wulkanowy:sdk:2840d9d6d0"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt
index 082c847e5..8dcade56e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryAdapter.kt
@@ -10,6 +10,7 @@ import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.databinding.ItemGradeSummaryBinding
import io.github.wulkanowy.databinding.ScrollableHeaderGradeSummaryBinding
+import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid
import io.github.wulkanowy.utils.calcFinalAverage
import java.util.Locale
import javax.inject.Inject
@@ -61,7 +62,7 @@ class GradeSummaryAdapter @Inject constructor(
if (items.isEmpty()) return
val context = binding.root.context
- val finalItemsCount = items.count { it.finalGrade.matches("[0-6][+-]?".toRegex()) }
+ val finalItemsCount = items.count { isGradeValid(it.finalGrade) }
val calculatedItemsCount = items.count { value -> value.average != 0.0 }
val allItemsCount = items.count { !it.subject.equals("zachowanie", true) }
val finalAverage = items.calcFinalAverage(
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 ff65d6376..61924d4e9 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/GradeExtension.kt
@@ -4,6 +4,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.enums.GradeColorTheme
+import io.github.wulkanowy.sdk.scrapper.grades.getGradeValueWithModifier
import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid
fun List.calcAverage(isOptionalArithmeticAverage: Boolean): Double {
@@ -20,20 +21,15 @@ fun List.calcAverage(isOptionalArithmeticAverage: Boolean): Double {
}
fun List.calcFinalAverage(plusModifier: Double, minusModifier: Double) = asSequence()
- .mapNotNull {
- if (it.finalGrade.matches("[0-6][+-]?".toRegex())) {
- when {
- it.finalGrade.endsWith('+') -> {
- it.finalGrade.removeSuffix("+").toDouble() + plusModifier
- }
- it.finalGrade.endsWith('-') -> {
- it.finalGrade.removeSuffix("-").toDouble() - minusModifier
- }
- else -> {
- it.finalGrade.toDouble()
- }
- }
- } else null
+ .mapNotNull { summary ->
+ val (gradeValue, gradeModifier) = getGradeValueWithModifier(summary.finalGrade)
+ if (gradeValue == null || gradeModifier == null) return@mapNotNull null
+
+ when {
+ gradeModifier > 0 -> gradeValue + plusModifier
+ gradeModifier < 0 -> gradeValue - minusModifier
+ else -> gradeValue + 0.0
+ }
}
.average()
.let { if (it.isNaN()) 0.0 else it }
From a523850216a5d9dc82dc3fda9a73e94507c172f8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 28 Sep 2022 23:34:06 +0200
Subject: [PATCH 232/669] Bump annotation from 1.4.0 to 1.5.0 (#1998)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 1b745f0b6..3127d55c5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -198,7 +198,7 @@ dependencies {
implementation "androidx.activity:activity-ktx:1.5.1"
implementation "androidx.appcompat:appcompat:1.5.1"
implementation "androidx.fragment:fragment-ktx:1.5.3"
- implementation "androidx.annotation:annotation:1.4.0"
+ implementation "androidx.annotation:annotation:1.5.0"
implementation "androidx.preference:preference-ktx:1.2.0"
implementation "androidx.recyclerview:recyclerview:1.2.1"
From 8114a2376ee3f798e950b4a2deab3ad21c946f19 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 28 Sep 2022 21:43:45 +0000
Subject: [PATCH 233/669] Bump gradle from 7.2.2 to 7.3.0 (#1985)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index e308c579b..739d5752e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
- classpath 'com.android.tools.build:gradle:7.2.2'
+ classpath 'com.android.tools.build:gradle:7.3.0'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.14'
classpath 'com.huawei.agconnect:agcp:1.7.2.300'
From 4dc80595ac9d47e8a07fe998e14dffcee83d5885 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 4 Oct 2022 07:49:22 +0000
Subject: [PATCH 234/669] Bump robolectric from 4.8.2 to 4.9 (#2007)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 3127d55c5..ef5b99500 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -263,7 +263,7 @@ dependencies {
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
- testImplementation 'org.robolectric:robolectric:4.8.2'
+ testImplementation 'org.robolectric:robolectric:4.9'
testImplementation "androidx.test:runner:1.4.0"
testImplementation "androidx.test.ext:junit:1.1.3"
testImplementation "androidx.test:core:1.4.0"
From 95a90a7a79f2b2ef0510baaf305257246997f6f4 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 4 Oct 2022 07:49:41 +0000
Subject: [PATCH 235/669] Bump mockk from 1.13.1 to 1.13.2 (#2006)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index ef5b99500..458f963fb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -181,7 +181,7 @@ ext {
android_hilt = "1.0.0"
room = "2.4.3"
chucker = "3.5.2"
- mockk = "1.13.1"
+ mockk = "1.13.2"
coroutines = "1.6.4"
}
From c65303959072a7e4b009973ed593abfd7f6657c2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 4 Oct 2022 07:50:05 +0000
Subject: [PATCH 236/669] Bump coil from 2.2.1 to 2.2.2 (#2004)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 458f963fb..cd4f6cc12 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -236,7 +236,7 @@ dependencies {
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
- implementation "io.coil-kt:coil:2.2.1"
+ implementation "io.coil-kt:coil:2.2.2"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
From 37f7f21a033e03ccb976dc8c6c267ef68061cd25 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Tue, 4 Oct 2022 09:51:30 +0200
Subject: [PATCH 237/669] Reorder action buttons on the message preview screen
to hide the forward button in overflow menu (#2000)
---
.../res/menu/action_menu_message_preview.xml | 28 +++++++++----------
1 file changed, 14 insertions(+), 14 deletions(-)
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 5011e2356..57cf05ddb 100644
--- a/app/src/main/res/menu/action_menu_message_preview.xml
+++ b/app/src/main/res/menu/action_menu_message_preview.xml
@@ -8,20 +8,6 @@
android:title="@string/message_reply"
app:iconTint="@color/material_on_surface_emphasis_medium"
app:showAsAction="ifRoom" />
-
-
+
+
From cd037f0ce0f2f2b0107af5114ed3c11f1d03388e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 4 Oct 2022 07:58:58 +0000
Subject: [PATCH 238/669] Bump kotlin_version from 1.7.10 to 1.7.20 (#2003)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 739d5752e..fb58a44d2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
buildscript {
ext {
- kotlin_version = '1.7.10'
+ kotlin_version = '1.7.20'
about_libraries = '10.4.0'
hilt_version = "2.44"
}
From 3f431022a5b5316babaa89951d13a67d5680299e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 4 Oct 2022 08:08:21 +0000
Subject: [PATCH 239/669] Bump about_libraries from 10.4.0 to 10.5.0 (#2005)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index fb58a44d2..9f5b21f4d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
buildscript {
ext {
kotlin_version = '1.7.20'
- about_libraries = '10.4.0'
+ about_libraries = '10.5.0'
hilt_version = "2.44"
}
repositories {
From ad487e680cf6907eba664d7922c12f184974f830 Mon Sep 17 00:00:00 2001
From: Daniel Olczyk <44818681+MRmlik12@users.noreply.github.com>
Date: Wed, 5 Oct 2022 22:25:09 +0200
Subject: [PATCH 240/669] Fix grade weight text truncation in grade dialog with
large font set (#2009)
---
app/src/main/res/layout/dialog_grade.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/layout/dialog_grade.xml b/app/src/main/res/layout/dialog_grade.xml
index 9c52c1d0b..94facb232 100644
--- a/app/src/main/res/layout/dialog_grade.xml
+++ b/app/src/main/res/layout/dialog_grade.xml
@@ -21,7 +21,7 @@
Date: Tue, 18 Oct 2022 19:39:16 +0000
Subject: [PATCH 241/669] Bump play-services-ads from 21.2.0 to 21.3.0 (#2016)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index cd4f6cc12..a0285de83 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -247,7 +247,7 @@ dependencies {
playImplementation 'com.google.firebase:firebase-crashlytics:'
playImplementation 'com.google.android.play:core:1.10.3'
playImplementation 'com.google.android.play:core-ktx:1.8.1'
- playImplementation 'com.google.android.gms:play-services-ads:21.2.0'
+ playImplementation 'com.google.android.gms:play-services-ads:21.3.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.8.0.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.2.300'
From 1f11eea9b58e66ce9c7aa6aba7e1dd3ad3ad6078 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 18 Oct 2022 19:39:36 +0000
Subject: [PATCH 242/669] Bump gradle from 7.3.0 to 7.3.1 (#2015)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 9f5b21f4d..5b7b4abbb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
- classpath 'com.android.tools.build:gradle:7.3.0'
+ classpath 'com.android.tools.build:gradle:7.3.1'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.14'
classpath 'com.huawei.agconnect:agcp:1.7.2.300'
From e20c232f8fd2797dd83fae0e6dbdeddde6b000d3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 18 Oct 2022 19:39:54 +0000
Subject: [PATCH 243/669] Bump kotlinx-serialization-json from 1.4.0 to 1.4.1
(#2014)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index a0285de83..4ea25d7e2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -190,7 +190,7 @@ dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
- implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0"
+ implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
implementation "androidx.core:core-ktx:1.8.0"
From 4c24363599f8dc486a1d7b9a1507977439ef9982 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 18 Oct 2022 19:40:28 +0000
Subject: [PATCH 244/669] Bump about_libraries from 10.5.0 to 10.5.1 (#2012)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 5b7b4abbb..5131796bb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
buildscript {
ext {
kotlin_version = '1.7.20'
- about_libraries = '10.5.0'
+ about_libraries = '10.5.1'
hilt_version = "2.44"
}
repositories {
From e91cd188044c94c1022f13f053d74cf243201d5c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 18 Oct 2022 19:41:15 +0000
Subject: [PATCH 245/669] Bump firebase-bom from 30.5.0 to 31.0.0 (#2013)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 4ea25d7e2..672a4becc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
- playImplementation platform('com.google.firebase:firebase-bom:30.5.0')
+ playImplementation platform('com.google.firebase:firebase-bom:31.0.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From a14c4b489bf1b413157500666c828fc4a178c7f5 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 26 Oct 2022 18:22:16 +0000
Subject: [PATCH 246/669] Bump material from 1.6.1 to 1.7.0 (#2022)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 672a4becc..8848a413b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -206,7 +206,7 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
- implementation "com.google.android.material:material:1.6.1"
+ implementation "com.google.android.material:material:1.7.0"
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation 'com.github.lopspower:CircularImageView:4.2.0'
From 4a484dc2ce25472961f0842cb3e05575233b1c00 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 26 Oct 2022 18:22:34 +0000
Subject: [PATCH 247/669] Bump fragment-ktx from 1.5.3 to 1.5.4 (#2020)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 8848a413b..b15eb651d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -197,7 +197,7 @@ dependencies {
implementation 'androidx.core:core-splashscreen:1.0.0'
implementation "androidx.activity:activity-ktx:1.5.1"
implementation "androidx.appcompat:appcompat:1.5.1"
- implementation "androidx.fragment:fragment-ktx:1.5.3"
+ implementation "androidx.fragment:fragment-ktx:1.5.4"
implementation "androidx.annotation:annotation:1.5.0"
implementation "androidx.preference:preference-ktx:1.2.0"
From 49b383fbe51078cc375d972aea6b11458906a35f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 26 Oct 2022 18:22:53 +0000
Subject: [PATCH 248/669] Bump firebase-bom from 31.0.0 to 31.0.1 (#2019)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index b15eb651d..6211db2d8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
- playImplementation platform('com.google.firebase:firebase-bom:31.0.0')
+ playImplementation platform('com.google.firebase:firebase-bom:31.0.1')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 3925a6261b9d921f2ea4b0f051b7092af90d9930 Mon Sep 17 00:00:00 2001
From: Damian Czupryn <60961958+Daxxxis@users.noreply.github.com>
Date: Wed, 26 Oct 2022 22:27:37 +0200
Subject: [PATCH 249/669] Add missing CS and SK links in German README (#2018)
---
README.de.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/README.de.md b/README.de.md
index b9e1d1ec1..e03557a06 100644
--- a/README.de.md
+++ b/README.de.md
@@ -2,6 +2,10 @@
[English version of README](README.en.md)
+[Česká verze README](README.cs.md)
+
+[Slovenská verzia README](README.sk.md)
+
# Wulkanowy
[](https://github.com/wulkanowy/wulkanowy/actions)
From 22a4f509dcc49086ea51939d0b820a479a01aa32 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Thu, 27 Oct 2022 12:41:33 +0200
Subject: [PATCH 250/669] Add installation id to crashlytics and bug report
emails (#2024)
---
.../github/wulkanowy/utils/AnalyticsHelper.kt | 14 +++-------
.../github/wulkanowy/utils/AnalyticsHelper.kt | 26 ++++++++++++++-----
.../github/wulkanowy/utils/CrashLogUtils.kt | 9 ++-----
.../repositories/PreferencesRepository.kt | 24 ++++++++---------
.../github/wulkanowy/ui/base/ErrorDialog.kt | 10 ++++---
.../ui/modules/about/AboutFragment.kt | 7 ++++-
.../modules/login/form/LoginFormFragment.kt | 7 ++++-
.../LoginStudentSelectFragment.kt | 10 +++++--
.../login/symbol/LoginSymbolFragment.kt | 7 ++++-
app/src/main/res/values/strings.xml | 4 +--
.../github/wulkanowy/utils/AnalyticsHelper.kt | 26 ++++++++++++++-----
.../wulkanowy/utils/CrashlyticsUtils.kt | 3 ---
12 files changed, 90 insertions(+), 57 deletions(-)
diff --git a/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
index 3bf7e1693..a3eed484a 100644
--- a/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
+++ b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
@@ -8,15 +8,7 @@ import javax.inject.Singleton
@Suppress("UNUSED_PARAMETER")
class AnalyticsHelper @Inject constructor() {
- fun logEvent(name: String, vararg params: Pair) {
- // do nothing
- }
-
- fun setCurrentScreen(activity: Activity, name: String?) {
- // do nothing
- }
-
- fun popCurrentScreen(name: String?) {
- // do nothing
- }
+ fun logEvent(name: String, vararg params: Pair) = Unit
+ fun setCurrentScreen(activity: Activity, name: String?) = Unit
+ fun popCurrentScreen(name: String?) = Unit
}
diff --git a/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
index 5d33825f1..1f78931ae 100644
--- a/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
+++ b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
@@ -3,26 +3,38 @@ package io.github.wulkanowy.utils
import android.app.Activity
import android.content.Context
import android.os.Bundle
+import com.huawei.agconnect.crash.AGConnectCrash
import com.huawei.hms.analytics.HiAnalytics
import dagger.hilt.android.qualifiers.ApplicationContext
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class AnalyticsHelper @Inject constructor(
- @ApplicationContext private val context: Context
+ @ApplicationContext private val context: Context,
+ preferencesRepository: PreferencesRepository,
+ appInfo: AppInfo,
) {
private val analytics by lazy { HiAnalytics.getInstance(context) }
+ private val connectCrash by lazy { AGConnectCrash.getInstance() }
+
+ init {
+ if (!appInfo.isDebug) {
+ connectCrash.setUserId(preferencesRepository.installationId)
+ }
+ }
+
fun logEvent(name: String, vararg params: Pair) {
Bundle().apply {
- params.forEach {
- if (it.second == null) return@forEach
- when (it.second) {
- is String, is String? -> putString(it.first, it.second as String)
- is Int, is Int? -> putInt(it.first, it.second as Int)
- is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean)
+ params.forEach { (key, value) ->
+ if (value == null) return@forEach
+ when (value) {
+ is String -> putString(key, value)
+ is Int -> putInt(key, value)
+ is Boolean -> putBoolean(key, value)
}
}
analytics.onEvent(name, this)
diff --git a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt
index b0c34f413..377e83666 100644
--- a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt
+++ b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt
@@ -3,6 +3,7 @@ package io.github.wulkanowy.utils
import android.util.Log
import com.huawei.agconnect.crash.AGConnectCrash
import fr.bipi.tressence.base.FormatterPriorityTree
+import fr.bipi.tressence.common.StackTraceRecorder
class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) {
@@ -22,16 +23,10 @@ class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR, ExceptionFilter)
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
if (skipLog(priority, tag, message, t)) return
- // Disabled due to a bug in the Huawei library
-
- /*connectCrash.setCustomKey("priority", priority)
- connectCrash.setCustomKey("tag", tag.orEmpty())
- connectCrash.setCustomKey("message", message)
-
if (t != null) {
connectCrash.recordException(t)
} else {
connectCrash.recordException(StackTraceRecorder(format(priority, tag, message)))
- }*/
+ }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
index 486538e0c..afc262868 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt
@@ -10,17 +10,16 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.enums.*
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.time.Instant
+import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
-@OptIn(ExperimentalCoroutinesApi::class)
@Singleton
class PreferencesRepository @Inject constructor(
@ApplicationContext val context: Context,
@@ -316,6 +315,16 @@ class PreferencesRepository @Inject constructor(
putBoolean(context.getString(R.string.pref_key_ads_enabled), value)
}
+ var installationId: String
+ get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty()
+ private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) }
+
+ init {
+ if (installationId.isEmpty()) {
+ installationId = UUID.randomUUID().toString()
+ }
+ }
+
private fun getLong(id: Int, default: Int) = getLong(context.getString(id), default)
private fun getLong(id: String, default: Int) =
@@ -331,23 +340,14 @@ class PreferencesRepository @Inject constructor(
private fun getBoolean(id: String, default: Int) =
sharedPref.getBoolean(id, context.resources.getBoolean(default))
- private fun getBoolean(id: Int, default: Boolean) =
- sharedPref.getBoolean(context.getString(id), default)
-
private companion object {
-
+ private const val PREF_KEY_INSTALLATION_ID = "installation_id"
private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position"
-
private const val PREF_KEY_IN_APP_REVIEW_COUNT = "in_app_review_count"
-
private const val PREF_KEY_IN_APP_REVIEW_DATE = "in_app_review_date"
-
private const val PREF_KEY_IN_APP_REVIEW_DONE = "in_app_review_done"
-
private const val PREF_KEY_APP_SUPPORT_SHOWN = "app_support_shown"
-
private const val PREF_KEY_PERSONALIZED_ADS_ENABLED = "personalized_ads_enabled"
-
private const val PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS = "admin_message_dismissed_ids"
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
index 48c003b7e..e979fa8af 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorDialog.kt
@@ -4,7 +4,6 @@ import android.app.Dialog
import android.content.ClipData
import android.content.ClipboardManager
import android.os.Bundle
-import android.view.LayoutInflater
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.appcompat.app.AlertDialog
@@ -15,6 +14,7 @@ import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.databinding.DialogErrorBinding
import io.github.wulkanowy.utils.*
import javax.inject.Inject
@@ -25,6 +25,9 @@ class ErrorDialog : DialogFragment() {
@Inject
lateinit var appInfo: AppInfo
+ @Inject
+ lateinit var preferencesRepository: PreferencesRepository
+
companion object {
private const val ARGUMENT_KEY = "error"
@@ -36,7 +39,7 @@ class ErrorDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val error = requireArguments().getSerializable(ARGUMENT_KEY) as Throwable
- val binding = DialogErrorBinding.inflate(LayoutInflater.from(context))
+ val binding = DialogErrorBinding.inflate(layoutInflater)
binding.bindErrorDetails(error)
return getAlertDialog(binding, error).apply {
@@ -99,7 +102,8 @@ class ErrorDialog : DialogFragment() {
R.string.about_feedback_template,
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
appInfo.systemVersion.toString(),
- "${appInfo.versionName}-${appInfo.buildFlavor}"
+ "${appInfo.versionName}-${appInfo.buildFlavor}",
+ preferencesRepository.installationId,
) + "\n" + content,
onActivityNotFound = {
requireContext().openInternetBrowser(
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 701656b55..d7f39e303 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
@@ -6,6 +6,7 @@ import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.databinding.FragmentAboutBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment
@@ -30,6 +31,9 @@ class AboutFragment : BaseFragment(R.layout.fragment_about
@Inject
lateinit var appInfo: AppInfo
+ @Inject
+ lateinit var preferencesRepository: PreferencesRepository
+
override val versionRes: Triple?
get() = context?.run {
val buildTimestamp =
@@ -185,7 +189,8 @@ class AboutFragment : BaseFragment(R.layout.fragment_about
R.string.about_feedback_template,
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
appInfo.systemVersion.toString(),
- "${appInfo.versionName}-${appInfo.buildFlavor}"
+ "${appInfo.versionName}-${appInfo.buildFlavor}",
+ preferencesRepository.installationId,
),
onActivityNotFound = {
requireContext().openInternetBrowser(
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 d31f5cf0f..463e192de 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
@@ -10,6 +10,7 @@ import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.databinding.FragmentLoginFormBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
@@ -32,6 +33,9 @@ class LoginFormFragment : BaseFragment(R.layout.fragme
@Inject
lateinit var appInfo: AppInfo
+ @Inject
+ lateinit var preferencesRepository: PreferencesRepository
+
companion object {
fun newInstance() = LoginFormFragment()
}
@@ -260,8 +264,9 @@ class LoginFormFragment : BaseFragment(R.layout.fragme
R.string.login_email_text,
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
appInfo.systemVersion.toString(),
- appInfo.versionName,
+ "${appInfo.versionName}-${appInfo.buildFlavor}",
"$formHostValue/$formHostSymbol",
+ preferencesRepository.installationId,
lastError
)
)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
index 6c910fe03..c42a4e9d1 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/studentselect/LoginStudentSelectFragment.kt
@@ -9,6 +9,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
@@ -32,6 +33,9 @@ class LoginStudentSelectFragment :
@Inject
lateinit var appInfo: AppInfo
+ @Inject
+ lateinit var preferencesRepository: PreferencesRepository
+
companion object {
const val ARG_STUDENTS = "STUDENTS"
@@ -111,10 +115,12 @@ class LoginStudentSelectFragment :
email = "wulkanowyinc@gmail.com",
subject = requireContext().getString(R.string.login_email_subject),
body = requireContext().getString(
- R.string.login_email_text, appInfo.systemModel,
+ R.string.login_email_text,
+ "${appInfo.systemManufacturer} ${appInfo.systemModel}",
appInfo.systemVersion.toString(),
- appInfo.versionName,
+ "${appInfo.versionName}-${appInfo.buildFlavor}",
"Select users to log in",
+ preferencesRepository.installationId,
lastError
)
)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt
index 58bdf6cef..36c40d156 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/symbol/LoginSymbolFragment.kt
@@ -13,6 +13,7 @@ import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.databinding.FragmentLoginSymbolBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginActivity
@@ -34,6 +35,9 @@ class LoginSymbolFragment :
@Inject
lateinit var appInfo: AppInfo
+ @Inject
+ lateinit var preferencesRepository: PreferencesRepository
+
companion object {
private const val SAVED_LOGIN_DATA = "LOGIN_DATA"
@@ -159,8 +163,9 @@ class LoginSymbolFragment :
R.string.login_email_text,
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
appInfo.systemVersion.toString(),
- appInfo.versionName,
+ "${appInfo.versionName}-${appInfo.buildFlavor}",
"$host/${binding.loginSymbolName.text}",
+ preferencesRepository.installationId,
lastError
)
)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e4542547d..48376d216 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -71,7 +71,7 @@
Discord
Send email
Zgłoszenie: Problemy z logowaniem
- Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nDodatkowe informacje: %4$s\nOstatni błąd: %5$s\n\nNazwa szkoły i miejscowość:
+ Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nDodatkowe informacje: %4$s\nIdentyfikator instalacji: %5$s\nOstatni błąd: %6$s\n\nNazwa szkoły i miejscowość:
Make sure you select the correct UONET+ register variation!
I forgot my password
Recover your account
@@ -512,7 +512,7 @@
Visit the website and help develop the application
Licenses
Licenses of libraries used in the application
- Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\n\nTreść zgłoszenia:
+ Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nIdentyfikator instalacji: %4$s\nTreść zgłoszenia:
diff --git a/app/src/play/java/io/github/wulkanowy/utils/AnalyticsHelper.kt b/app/src/play/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
index b65325790..3215fa20c 100644
--- a/app/src/play/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
+++ b/app/src/play/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
@@ -4,25 +4,37 @@ import android.app.Activity
import android.content.Context
import android.os.Bundle
import com.google.firebase.analytics.FirebaseAnalytics
+import com.google.firebase.crashlytics.FirebaseCrashlytics
import dagger.hilt.android.qualifiers.ApplicationContext
+import io.github.wulkanowy.data.repositories.PreferencesRepository
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class AnalyticsHelper @Inject constructor(
- @ApplicationContext private val context: Context
+ @ApplicationContext private val context: Context,
+ preferencesRepository: PreferencesRepository,
+ appInfo: AppInfo,
) {
private val analytics by lazy { FirebaseAnalytics.getInstance(context) }
+ private val crashlytics by lazy { FirebaseCrashlytics.getInstance() }
+
+ init {
+ if (!appInfo.isDebug) {
+ crashlytics.setUserId(preferencesRepository.installationId)
+ }
+ }
+
fun logEvent(name: String, vararg params: Pair) {
Bundle().apply {
- params.forEach {
- if (it.second == null) return@forEach
- when (it.second) {
- is String, is String? -> putString(it.first, it.second.toString())
- is Int, is Int? -> putInt(it.first, it.second as Int)
- is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean)
+ params.forEach { (key, value) ->
+ if (value == null) return@forEach
+ when (value) {
+ is String -> putString(key, value)
+ is Int -> putInt(key, value)
+ is Boolean -> putBoolean(key, value)
}
}
analytics.logEvent(name, this)
diff --git a/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt b/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt
index 410fddf16..f980bc4bb 100644
--- a/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt
+++ b/app/src/play/java/io/github/wulkanowy/utils/CrashlyticsUtils.kt
@@ -23,9 +23,6 @@ class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR, ExceptionFilter)
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
if (skipLog(priority, tag, message, t)) return
- crashlytics.setCustomKey("priority", priority)
- crashlytics.setCustomKey("tag", tag.orEmpty())
- crashlytics.setCustomKey("message", message)
if (t != null) {
crashlytics.recordException(t)
} else {
From 7bee10d5ce0aa85becaa5a5b2a8ecd752ae0b1cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 28 Oct 2022 11:08:40 +0200
Subject: [PATCH 251/669] Hide room view in timetable item if there is no room
in API (#2026)
---
.../github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
index d6917672a..2f0d697fc 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt
@@ -191,7 +191,7 @@ class TimetableAdapter @Inject constructor() :
)
} else {
timetableItemDescription.visibility = GONE
- timetableItemRoom.visibility = VISIBLE
+ timetableItemRoom.isVisible = lesson.room.isNotBlank() || lesson.roomOld.isNotBlank()
timetableItemGroup.isVisible = item.showGroupsInPlan && lesson.group.isNotBlank()
timetableItemTeacher.visibility = VISIBLE
}
From 515a3973b74048daba05783609b5530b59279938 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 28 Oct 2022 11:09:38 +0200
Subject: [PATCH 252/669] Use text color, font face and red dot to
differentiate unread messages (#2027)
---
.../modules/message/tab/MessageTabAdapter.kt | 33 ++++++++++++++-----
app/src/main/res/drawable/ic_circle.xml | 10 ++++++
app/src/main/res/layout/item_message.xml | 22 +++++++++++--
3 files changed, 54 insertions(+), 11 deletions(-)
create mode 100644 app/src/main/res/drawable/ic_circle.xml
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 55f03ef84..234d17eb2 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
@@ -1,15 +1,18 @@
package io.github.wulkanowy.ui.modules.message.tab
+import android.content.res.ColorStateList
import android.graphics.Typeface
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.CompoundButton
import androidx.core.view.isVisible
+import androidx.core.widget.ImageViewCompat
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.ItemMessageBinding
import io.github.wulkanowy.databinding.ItemMessageChipsBinding
+import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.toFormattedString
import javax.inject.Inject
@@ -85,21 +88,35 @@ class MessageTabAdapter @Inject constructor() :
val message = item.message
with(holder.binding) {
- val style = if (message.unread) Typeface.BOLD else Typeface.NORMAL
+ val normalFont = Typeface.create("sans-serif", Typeface.NORMAL)
+ val boldFont = Typeface.create("sans-serif-black", Typeface.NORMAL)
+
+ val primaryColor = root.context.getThemeAttrColor(android.R.attr.textColorPrimary)
+ val secondaryColor = root.context.getThemeAttrColor(android.R.attr.textColorSecondary)
+
+ val currentFont = if (message.unread) boldFont else normalFont
+ val currentTextColor = if (message.unread) primaryColor else secondaryColor
with(messageItemAuthor) {
text = message.correspondents
- setTypeface(null, style)
+ setTextColor(currentTextColor)
+ typeface = currentFont
}
- messageItemSubject.run {
+ with(messageItemSubject) {
text = message.subject.ifBlank { context.getString(R.string.message_no_subject) }
- setTypeface(null, style)
+ setTextColor(currentTextColor)
+ typeface = currentFont
}
- messageItemDate.run {
+ with(messageItemDate) {
text = message.date.toFormattedString()
- setTypeface(null, style)
+ setTextColor(currentTextColor)
+ typeface = currentFont
}
- messageItemAttachmentIcon.isVisible = message.hasAttachments
+ with(messageItemAttachmentIcon) {
+ ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(currentTextColor))
+ isVisible = message.hasAttachments
+ }
+ messageItemUnreadIndicator.isVisible = message.unread
root.setOnClickListener {
holder.bindingAdapterPosition.let {
@@ -111,7 +128,7 @@ class MessageTabAdapter @Inject constructor() :
root.setOnLongClickListener {
onLongItemClickListener(item)
- return@setOnLongClickListener true
+ true
}
with(messageItemCheckbox) {
diff --git a/app/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/ic_circle.xml
new file mode 100644
index 000000000..d2932fe62
--- /dev/null
+++ b/app/src/main/res/drawable/ic_circle.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_message.xml b/app/src/main/res/layout/item_message.xml
index c25faacc8..39fbaad01 100644
--- a/app/src/main/res/layout/item_message.xml
+++ b/app/src/main/res/layout/item_message.xml
@@ -30,6 +30,7 @@
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:singleLine="true"
+ android:textColor="?android:textColorSecondary"
android:textSize="15sp"
app:layout_constraintEnd_toStartOf="@+id/messageItemDate"
app:layout_constraintStart_toEndOf="@id/messageItemCheckbox"
@@ -40,10 +41,13 @@
android:id="@+id/messageItemDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
android:gravity="end"
+ android:textColor="?android:textColorSecondary"
android:textSize="13sp"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/messageItemUnreadIndicator"
app:layout_constraintTop_toTopOf="parent"
+ app:layout_goneMarginEnd="0dp"
tools:text="@tools:sample/date/ddmmyy" />
+
+
From c5e2b18695c2a1f58afd407d399d927d03573dd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 28 Oct 2022 11:10:05 +0200
Subject: [PATCH 253/669] Fix SSL certificate out-of-date detection (#2028)
---
.../java/io/github/wulkanowy/utils/ExceptionExtension.kt | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
index 43cecd400..a4c2537ac 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
@@ -15,16 +15,17 @@ import java.net.ConnectException
import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
+import java.security.cert.CertPathValidatorException
import java.security.cert.CertificateExpiredException
import java.security.cert.CertificateNotYetValidException
import javax.net.ssl.SSLHandshakeException
fun Resources.getErrorString(error: Throwable): String = when (error) {
is UnknownHostException -> R.string.error_no_internet
+ is ConnectException,
is SocketException,
is SocketTimeoutException,
is InterruptedIOException,
- is ConnectException,
is StreamResetException -> R.string.error_timeout
is NotLoggedInException -> R.string.error_login_failed
is PasswordChangeRequiredException -> R.string.error_password_change_required
@@ -42,10 +43,10 @@ fun Resources.getErrorString(error: Throwable): String = when (error) {
fun Throwable.isShouldBeReported(): Boolean = when (this) {
is UnknownHostException,
+ is ConnectException,
is SocketException,
is SocketTimeoutException,
is InterruptedIOException,
- is ConnectException,
is StreamResetException,
is ServiceUnavailableException,
is FeatureDisabledException,
@@ -70,5 +71,6 @@ private fun Throwable?.isCausedByCertificateNotValidNow(): Boolean {
private fun Throwable?.isCertificateNotValidNow(): Boolean {
val isNotYetValid = this is CertificateNotYetValidException
val isExpired = this is CertificateExpiredException
- return isNotYetValid || isExpired
+ val isInvalidPath = this is CertPathValidatorException
+ return isNotYetValid || isExpired || isInvalidPath
}
From ffd5addadb3f1250f438ad3cea2e2889649e31b8 Mon Sep 17 00:00:00 2001
From: Damian Czupryn <60961958+Daxxxis@users.noreply.github.com>
Date: Fri, 28 Oct 2022 11:10:35 +0200
Subject: [PATCH 254/669] Add Crowdin badges to README (#2025)
---
README.cs.md | 1 +
README.de.md | 1 +
README.en.md | 1 +
README.md | 1 +
README.sk.md | 1 +
5 files changed, 5 insertions(+)
diff --git a/README.cs.md b/README.cs.md
index 5c1e5ea71..0d6da9c36 100644
--- a/README.cs.md
+++ b/README.cs.md
@@ -13,6 +13,7 @@
[](https://discord.gg/vccAQBr)
[](https://f-droid.org/packages/io.github.wulkanowy/)
[](https://github.com/wulkanowy/wulkanowy/releases)
+[](https://translate.wulkanowy.net.pl)
Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče
diff --git a/README.de.md b/README.de.md
index e03557a06..9c25bad65 100644
--- a/README.de.md
+++ b/README.de.md
@@ -13,6 +13,7 @@
[](https://discord.gg/vccAQBr)
[](https://f-droid.org/packages/io.github.wulkanowy/)
[](https://github.com/wulkanowy/wulkanowy/releases)
+[](https://translate.wulkanowy.net.pl)
Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre Eltern
diff --git a/README.en.md b/README.en.md
index 1ac2a6721..708d30bdb 100644
--- a/README.en.md
+++ b/README.en.md
@@ -13,6 +13,7 @@
[](https://discord.gg/vccAQBr)
[](https://f-droid.org/packages/io.github.wulkanowy/)
[](https://github.com/wulkanowy/wulkanowy/releases)
+[](https://translate.wulkanowy.net.pl)
Unofficial android VULCAN UONET+ register client for both students and their parents
diff --git a/README.md b/README.md
index e7c7d4c5e..86b83552a 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@
[](https://discord.gg/vccAQBr)
[](https://f-droid.org/packages/io.github.wulkanowy/)
[](https://github.com/wulkanowy/wulkanowy/releases)
+[](https://translate.wulkanowy.net.pl)
Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
diff --git a/README.sk.md b/README.sk.md
index 2f3ba41dd..d318936aa 100644
--- a/README.sk.md
+++ b/README.sk.md
@@ -13,6 +13,7 @@
[](https://discord.gg/vccAQBr)
[](https://f-droid.org/packages/io.github.wulkanowy/)
[](https://github.com/wulkanowy/wulkanowy/releases)
+[](https://translate.wulkanowy.net.pl)
Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov
From b269360ecb852bfaf9eb3b97c762e7e7c689c9e1 Mon Sep 17 00:00:00 2001
From: Damian Czupryn <60961958+Daxxxis@users.noreply.github.com>
Date: Sun, 30 Oct 2022 03:00:39 +0100
Subject: [PATCH 255/669] Langs placement in README adjustments (#2029)
---
README.cs.md | 10 ++--------
README.de.md | 8 +-------
README.en.md | 8 +-------
README.md | 8 +-------
README.sk.md | 10 ++--------
5 files changed, 7 insertions(+), 37 deletions(-)
diff --git a/README.cs.md b/README.cs.md
index 0d6da9c36..2b0dc12ea 100644
--- a/README.cs.md
+++ b/README.cs.md
@@ -1,10 +1,4 @@
-[English version of README](README.en.md)
-
-[Deutsche Version von README](README.de.md)
-
-[Polska wersja README](README.md)
-
-[Slovenská verzia README](README.sk.md)
+Česká verze / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
# Wulkanowy
@@ -58,7 +52,7 @@ Aktuální verzi si můžete stáhnout z Google Play, F-Droid nebo Huawei AppGal
Můžete si také stáhnout [vývojovou verzi](https://wulkanowy.github.io/#download), která zahrnuje nové funkce připravované pro příští vydání
-## Postaveno s
+## Postaveno s pomocí
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
diff --git a/README.de.md b/README.de.md
index 9c25bad65..6df10ecd0 100644
--- a/README.de.md
+++ b/README.de.md
@@ -1,10 +1,4 @@
-[Polska wersja README](README.md)
-
-[English version of README](README.en.md)
-
-[Česká verze README](README.cs.md)
-
-[Slovenská verzia README](README.sk.md)
+[Česká verze](README.cs.md) / Deutsche Version / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
# Wulkanowy
diff --git a/README.en.md b/README.en.md
index 708d30bdb..417b74de0 100644
--- a/README.en.md
+++ b/README.en.md
@@ -1,10 +1,4 @@
-[Polska wersja README](README.md)
-
-[Deutsche Version von README](README.de.md)
-
-[Česká verze README](README.cs.md)
-
-[Slovenská verzia README](README.sk.md)
+[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / English version / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
# Wulkanowy
diff --git a/README.md b/README.md
index 86b83552a..75b6cfca2 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,4 @@
-[English version of README](README.en.md)
-
-[Deutsche Version von README](README.de.md)
-
-[Česká verze README](README.cs.md)
-
-[Slovenská verzia README](README.sk.md)
+[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / Polska wersja / [Slovenská verzia](README.sk.md)
# Wulkanowy
diff --git a/README.sk.md b/README.sk.md
index d318936aa..240f8835c 100644
--- a/README.sk.md
+++ b/README.sk.md
@@ -1,10 +1,4 @@
-[English version of README](README.en.md)
-
-[Deutsche Version von README](README.de.md)
-
-[Polska wersja README](README.md)
-
-[Česká verze README](README.cs.md)
+[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / Slovenská verzia
# Wulkanowy
@@ -58,7 +52,7 @@ Aktuálnu verziu si môžete stiahnuť z Google Play, F-Droid alebo Huawei AppGa
Môžete si tiež stiahnuť [vývojovú verziu](https://wulkanowy.github.io/#download), ktorá zahrňuje nové funkcie pripravované pre budúce vydanie
-## Postavené s
+## Postavené s pomocou
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
From d924902dacac95123477dd6410c62430785aaa3c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 1 Nov 2022 19:49:56 +0000
Subject: [PATCH 256/669] Bump CircularImageView from 4.2.0 to 4.3.0 (#2036)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 6211db2d8..d7fcfa8f7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -209,7 +209,7 @@ dependencies {
implementation "com.google.android.material:material:1.7.0"
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
- implementation 'com.github.lopspower:CircularImageView:4.2.0'
+ implementation 'com.github.lopspower:CircularImageView:4.3.0'
implementation "androidx.work:work-runtime-ktx:$work_manager"
playImplementation "androidx.work:work-gcm:$work_manager"
From 4113bd9b538428cb6465cc3380b4b4d2f25ba9b1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 1 Nov 2022 19:50:17 +0000
Subject: [PATCH 257/669] Bump agconnect-crash from 1.7.2.300 to 1.7.3.300
(#2035)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index d7fcfa8f7..c250e7ead 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -250,7 +250,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:21.3.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.8.0.300'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.2.300'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.3.300'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From 86fe2b61cb9c158e53682974c11484343141e003 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 1 Nov 2022 19:50:37 +0000
Subject: [PATCH 258/669] Bump agcp from 1.7.2.300 to 1.7.3.300 (#2034)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 5131796bb..fa66d9304 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.14'
- classpath 'com.huawei.agconnect:agcp:1.7.2.300'
+ classpath 'com.huawei.agconnect:agcp:1.7.3.300'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4"
From 02cd4e4e06850b95a845b45db8c2b7f0194061ad Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 1 Nov 2022 19:50:59 +0000
Subject: [PATCH 259/669] Bump sonarqube-gradle-plugin from 3.4.0.2513 to
3.5.0.2730 (#2033)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index fa66d9304..3773dbc38 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,7 +20,7 @@ buildscript {
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4"
- classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513"
+ classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.5.0.2730"
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries"
}
From 21fe20924643fa4c6db0aa05b2bbee30d48ba071 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 1 Nov 2022 19:51:28 +0000
Subject: [PATCH 260/669] Bump firebase-bom from 31.0.1 to 31.0.2 (#2032)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index c250e7ead..a8f68884e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
- playImplementation platform('com.google.firebase:firebase-bom:31.0.1')
+ playImplementation platform('com.google.firebase:firebase-bom:31.0.2')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 62b7d42a73d0f422b02995f049449966020f0df6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Tue, 1 Nov 2022 20:57:05 +0100
Subject: [PATCH 261/669] New Crowdin updates (#1966)
---
app/src/main/res/values-de/strings.xml | 14 +-
.../res/values-es-rES/preferences_values.xml | 65 ++
app/src/main/res/values-es-rES/strings.xml | 715 ++++++++++++++++++
app/src/main/res/values-uk/strings.xml | 38 +-
4 files changed, 806 insertions(+), 26 deletions(-)
create mode 100644 app/src/main/res/values-es-rES/preferences_values.xml
create mode 100644 app/src/main/res/values-es-rES/strings.xml
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 4dfeee4f6..7b544258a 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -632,10 +632,10 @@
Antwort mit Nachrichtenhistorie
Arithmetisches Mittel anzeigen, wenn keine Gewichte angegeben sind
Unterstützung
- Privacy Policy
- Agreements
- Consent to processing of data related to ads
- Show ads in app
+ Datenschutz-Bestimmungen
+ Vereinbarungen
+ Zustimmung zur Verarbeitung von Daten im Zusammenhang mit Anzeigen
+ Anzeigen in der App anzeigen
Einzelanzeige ansehen, um Projekt zu unterstützen
Einwilligung in die Datenverarbeitung
Um eine Anzeige zu sehen, müssen Sie mit den Datenverarbeitungsbedingungen unserer Datenschutzerklärung einverstanden sein
@@ -647,9 +647,9 @@
Sie können Ihre Wahl jederzeit in den App-Einstellungen ändern. Wir verwenden Ihre Daten, um auf Sie zugeschnittene Anzeigen anzuzeigen oder unter Verwendung weniger Ihrer Daten nicht personalisierte Werbung anzuzeigen. Bitte lesen Sie unsere Datenschutzerklärung für Details
Personalisierte Werbung
keine personalisierte Werbung
- I am over 18 years old
- Yes, personalized ads
- Yes, non-personalized ads
+ Ich bin über 18 Jahre alt
+ Ja, personalisierte Werbung
+ Ja, nicht personalisierte Werbung
Erweitert
Aussehen & Verhalten
Benachrichtigungen
diff --git a/app/src/main/res/values-es-rES/preferences_values.xml b/app/src/main/res/values-es-rES/preferences_values.xml
new file mode 100644
index 000000000..ac2b6e9e5
--- /dev/null
+++ b/app/src/main/res/values-es-rES/preferences_values.xml
@@ -0,0 +1,65 @@
+
+
+
+ - Light
+ - Dark
+ - Black (AMOLED)
+
+
+ - System language
+ - Polski
+ - English
+ - Pусский
+ - Українська
+ - Deutsch
+ - Čeština
+ - Slovenčina
+
+
+ - 15 minutes
+ - 30 minutes
+ - 1 hour
+ - 2 hours
+ - 6 hours
+ - 12 hours
+ - 24 hours
+
+
+ - 0,00
+ - 0,25
+ - 0,33
+ - 0,5
+ - 0,75
+
+
+ - Alphabetically
+ - By date
+ - By average
+
+
+ - Dzienniczek+
+ - Wulkanowy
+ - Grade colors in register
+
+
+ - Up to 1 at once
+ - Always expanded
+ - Unlimited expansions
+
+
+ - Average of grades only from selected semester
+ - Average of averages from both semesters
+ - Average of grades from the whole year
+
+
+ - Lucky number
+ - Unread messages
+ - Attendance
+ - Lessons
+ - Grades
+ - Homework
+ - School announcements
+ - Exams
+ - Conferences
+
+
diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml
new file mode 100644
index 000000000..aecc720b7
--- /dev/null
+++ b/app/src/main/res/values-es-rES/strings.xml
@@ -0,0 +1,715 @@
+
+
+
+ Login
+ Wulkanowy
+ Grades
+ Attendance
+ Exams
+ Timetable
+ Settings
+ More
+ About
+ Log viewer
+ Debug
+ Notification debug
+ Contributors
+ Licenses
+ Messages
+ New message
+ New homework
+ Notes and achievements
+ Homework
+ Accounts manager
+ Select account
+ Account details
+ Student info
+ Dashboard
+ Notifications center
+
+ Semester %1$d, %2$d/%3$d
+
+ Sign in with the student or parent account
+ Enter the symbol from the register page for account: <b>%1$s</b>
+ Username
+ Email
+ Login, PESEL or e-mail
+ Password
+ UONET+ register variant
+ Mobile API
+ Scraper
+ Hybrid
+ Token
+ PIN
+ Symbol
+ Sign in
+ Password too short
+ Login details are incorrect
+ %1$s. Make sure the correct UONET+ register variation is selected below
+ Invalid PIN
+ Invalid token
+ Token expired
+ Invalid email
+ Use the assigned login instead of email
+ Use the assigned login or email in @%1$s
+ Invalid symbol
+ Student not found. Validate the symbol and the chosen variation of the UONET+ register
+ Selected student is already logged in
+ The symbol can be found on the register page in Uczeń → Dostęp Mobilny → Zarejestruj urządzenie mobilne.\n\nMake sure that you have set the appropriate register variant in the UONET+ register variant field on the previous screen
+ Select students to log in to the application
+ Other options
+ In this mode, a lucky number does not work, a class grade stats, summary of attendance, excuse for absence, completed lessons, school information and preview of the list of registered devices
+ This mode displays the same data as it appears on the register website
+ The combination of the best features of the other two modes. It works faster than scraper and provides features not available in the Mobile API mode. It is in the experimental phase
+ Privacy policy
+ Trouble signing in? Contact us!
+ Email
+ Discord
+ Send email
+ Make sure you select the correct UONET+ register variation!
+ I forgot my password
+ Recover your account
+ Recover
+ Student is already signed in
+ Standard
+
+ Account manager
+ Log in
+ Session expired
+ Session expired, log in again
+ Application support
+ Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time
+ Enable ads
+
+ Grade
+ Semester %d
+ Change semester
+ No grades
+ Weight
+ Weight: %s
+ Comment
+ Number of new ratings: %1$d
+ Average: %1$.2f
+ Points: %s
+ No average
+ Total points
+ Final grade
+ Predicted grade
+ Calculated average
+ How does Calculated Average work?
+ The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages
+ How does the Final Average work?
+ The Final Average is the arithmetic average calculated from all currently available final grades in the given semester.\n\nThe calculation scheme consists of the following steps:\n1. Summing up the final grades given by teachers\n2. Divide by the number of subjects that have already been graded
+ Final average
+ from %1$d of %2$d subjects
+ Summary
+ Class
+ Mark as read
+ Partial
+ Semester
+ Points
+ Legend
+ Class average: %1$s
+ Your average: %1$s
+ Your grade: %1$s
+ Class
+ Student
+
+ - %d grade
+ - %d grades
+
+
+ - New grade
+ - New grades
+
+
+ - New predicted grade
+ - New predicted grades
+
+
+ - New final grade
+ - New final grades
+
+
+ - You received %1$d grade
+ - You received %1$d grades
+
+
+ - You received %1$d predicted grade
+ - You received %1$d predicted grades
+
+
+ - You received %1$d final grade
+ - You received %1$d final grades
+
+
+ Lesson
+ Room
+ Group
+ Hours
+ Changes
+ No lessons this day
+ %s min
+ %s sec
+ %1$s left
+ in %1$s
+ Finished
+ Now: %s
+ Next: %s
+ Later: %s
+ %1$s lesson %2$d - %3$s
+ Change of room from %1$s to %2$s
+ Change of teacher from %1$s to %2$s
+ Change of subject from %1$s to %2$s
+
+ - Timetable change
+ - Timetable changes
+
+
+ - %1$s - %2$d change in timetable
+ - %1$s - %2$d changes in timetable
+
+
+ - %1$d change in timetable
+ - %1$d changes in timetable
+
+
+ - %d change
+ - %d changes
+
+
+ Completed lessons
+ Show completed lessons
+ No info about completed lessons
+ Topic
+ Absence
+ Resources
+
+ Additional lessons
+ Show additional lessons
+ No info about additional lessons
+ New lesson
+ New additional lesson
+ Additional lesson added successfully
+ Additional lesson deleted successfully
+ Repeat weekly
+ Delete additional lesson
+ Just this lesson
+ All in the series
+ Start time
+ End time
+ End time must be greater than start time
+
+ Attendance summary
+ Absent for school reasons
+ Excused absence
+ Unexcused absence
+ Exemption
+ Excused lateness
+ Unexcused lateness
+ Present
+ Deleted
+ Unknown
+ Number of lesson
+ No entries
+ Absence reason (optional)
+ Send
+ Absence excuse request sent successfully!
+ You must select at least one absence!
+ Excuse
+
+ - New attendance
+ - New attendance
+
+
+ - %1$d new attendance
+ - %1$d attendance
+
+
+ - %d attendance
+ - %d attendance
+
+
+ Total
+
+ No exams this week
+ Type
+ Entry date
+
+ - New exam
+ - New exams
+
+
+ - %d new exam
+ - %d new exams
+
+
+ - %d exam
+ - %d exams
+
+
+ Inbox
+ Sent
+ Trash
+ (no subject)
+ No messages
+ From:
+ To:
+ Date: %1$s
+ Reply
+ Forward
+ Select all
+ Unselect all
+ Move to trash
+ Delete permanently
+ Message deleted successfully
+ student
+ parent
+ guardian
+ employee
+ Share
+ Print
+ Subject
+ Content
+ Message sent successfully
+ Message does not exist
+ You need to choose at least 1 recipient
+ The message content must be at least 3 characters
+ Only unread
+ Only with attachments
+ Read: %s
+
+ - %1$d message
+ - %1$d messages
+
+
+ - New message
+ - New messages
+
+ Do you want to restore draft message?
+ Do you want to restore draft message with recipients: %s?
+
+ - You received %1$d message
+ - You received %1$d messages
+
+
+ - %1$d selected
+ - %1$d selected
+
+ Messages deleted
+
+ No info about notes
+ Points
+
+ - %d note
+ - %d notes
+
+
+ - New note
+ - New notes
+
+
+ - You received %1$d note
+ - You received %1$d notes
+
+
+
+ - %d praise
+ - %d praises
+
+
+ - New praise
+ - New praises
+
+
+ - You received %1$d praise
+ - You received %1$d praises
+
+
+
+ - %d neutral note
+ - %d neutral notes
+
+
+ - New neutral note
+ - New neutral notes
+
+
+ - You received %1$d neutral note
+ - You received %1$d neutral notes
+
+
+ No info about homework
+ Mark as done
+ Mark as undone
+ Add homework
+ Homework added successfully
+ Homework deleted successfully
+ Attachments
+
+ - New homework
+ - New homework
+
+
+ - You received %d new homework
+ - You received %d new homework
+
+
+ - %d homework
+ - %d homework
+
+
+ Lucky number
+ Today\'s lucky number is
+ No info about the lucky number
+ Lucky number for today
+ Today\'s lucky number is: %s
+ Show history
+
+ Lucky number history
+ No info about lucky numbers
+
+ Mobile devices
+ No devices
+ Deregister
+ Device removed
+ QR code
+ Token
+ Symbol
+ PIN
+
+ School and teachers
+
+ School
+ No info about school
+ School name
+ School address
+ Telephone
+ Name of headmaster
+ Name of pedagogue
+ Show on map
+ Call
+
+ Teachers
+ No info about teachers
+ No subject
+
+ Conferences
+ No info about conferences
+
+ - %d conference
+ - %d conferences
+
+
+ - New conference
+ - New conferences
+
+
+ - You have %1$d new conference
+ - You have %1$d new conferences
+
+ Present at conference
+ Agenda
+
+ School announcements
+ No school announcements
+
+ - %d school announcement
+ - %d school announcements
+
+
+ - New school announcement
+ - New school announcements
+
+
+ - You have %1$d new school announcement
+ - You have %1$d new school announcements
+
+
+ Add account
+ Logout
+ Do you want to log out this student?
+ Student logout
+ Student account
+ Parent account
+ Edit data
+ Accounts manager
+ Select student
+ Family
+ Contact
+ Residence details
+ Personal information
+
+ App version
+ Contributors
+ List of Wulkanowy developers
+ Report a bug
+ Send a bug report via e-mail
+ FAQ
+ Read Frequently Asked Questions
+ Discord server
+ Join the Wulkanowy community
+ Facebook fanpage
+ Twitter page
+ Follow us on twitter
+ Like our facebook fanpage
+ Privacy policy
+ Rules for collecting personal data
+ System settings
+ Open system settings
+ Homepage
+ Visit the website and help develop the application
+ Licenses
+ Licenses of libraries used in the application
+
+ License
+
+ Avatar
+ See more on GitHub
+
+ No info about student or student family
+ Name
+ Second name
+ Gender
+ Polish citizenship
+ Family name
+ Mother\'s and father\'s names
+ Phone
+ Cellphone
+ E-mail
+ Address of residence
+ Address of registration
+ Correspondence address
+ Surname and first name
+ Degree of kinship
+ Address
+ Phones
+ Male
+ Female
+ Last name
+ Guardian
+
+ Nick
+ Add nick
+ Choose avatar color
+
+ Share logs
+ Refresh
+
+ Lessons
+ (Tomorrow)
+ (Today and tomorrow)
+ In a moment:
+ Soon:
+ First:
+ Now:
+ End of lessons
+ Next:
+ Later:
+
+ - %1$d more lesson
+ - %1$d more lessons
+
+ until %1$s
+ No upcoming lessons
+ An error occurred while loading the lessons
+ Homework
+ No homework to do
+ An error occurred while loading the homework
+
+ - %1$d more homework
+ - %1$d more homework
+
+ due %1$s
+ Last grades
+ No new grades
+ An error occurred while loading the grades
+ School announcements
+ No current announcements
+ An error occurred while loading the announcements
+
+ - %1$d more announcement
+ - %1$d more announcements
+
+ Exams
+ No upcoming exams
+ An error occurred while loading the exams
+
+ - %1$d more exam
+ - %1$d more exams
+
+ Conferences
+ No upcoming conferences
+ An error occurred while loading the conferences
+
+ - %1$d more conference
+ - %1$d more conferences
+
+ An error occurred while loading data
+ None
+
+ Check for updates
+ Before reporting a bug, check first if an update with the bug fix is available
+
+ Content
+ Retry
+ Description
+ No description
+ Teacher
+ Date
+ Entry date
+ Color
+ Details
+ Category
+ Close
+ No data
+ Subject
+ Prev
+ Next
+ Search
+ Search…
+ Yes
+ No
+ Save
+ Title
+ Add
+ Copied
+ Undo
+ Change
+ Add to calendar
+
+ No lessons
+ Choose theme
+ Light
+ Dark
+ System Theme
+
+ App
+ Default view
+ Calculated average options
+ Force average calculation by app
+ Show presence
+ Theme
+ Grades expanding
+ Mark current lesson
+ Show groups next to subjects
+ Show chart list in class grades
+ Show subjects without grades
+ Grades color scheme
+ Subjects sorting
+ Language
+ Notifications
+ Other
+ Show notifications
+ Show upcoming lesson notifications
+ Make upcoming lesson notification persistent
+ Turn off when notification is not showing in your watch/band
+ Open system notification settings
+ Fix synchronization & notifications issues
+ Your device may have data synchronization issues and with notifications.\n\nTo fix them, you need to add Wulkanowy to the autostart and turn off battery optimization/saving in the phone settings.
+ Show debug notifications
+ Synchronization is disabled
+ Official app notifications
+ Capture official app notifications
+ Remove official app notifications after capture
+ Capture notifications
+ With this feature you can gain a substitute of push notifications like in the official app. All you need to do is allow Wulkanowy to receive all notifications in your system settings.\n\nHow it works?\nWhen you get a notification in Dziennik VULCAN, Wulkanowy will be notified (that\'s what these extra permissions are for) and will trigger a sync so that can send its own notification.\n\nFOR ADVANCED USERS ONLY
+ Upcoming lesson notifications
+ You must allow the Wulkanowy app to set alarms and reminders in your system settings to use this feature.
+ Go to settings
+ Synchronization
+ Automatic update
+ Suspended on holidays
+ Updates interval
+ Wi-Fi only
+ Sync now
+ Synced!
+ Sync failed
+ Sync in progress
+ Last full sync: %s
+ Value of the plus
+ Value of the minus
+ Reply with message history
+ Show arithmetic average when no weights provided
+ Support
+ Privacy Policy
+ Agreements
+ Consent to processing of data related to ads
+ Show ads in app
+ Watch single ad to support project
+ Consent to data processing
+ To view an advertisement you must agree to the data processing terms of our Privacy Policy
+ Agree
+ Privacy policy
+ Ad is loading
+ Thank you for your support, come back later for more ads
+ Can we use your data to display ads?
+ You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details
+ Personalized ads
+ Non-personalized ads
+ I am over 18 years old
+ Yes, personalized ads
+ Yes, non-personalized ads
+ Advanced
+ Appearance & Behavior
+ Notifications
+ Synchronization
+ Advertisements
+ Grades
+ Dashboard
+ Tiles visibility
+ Attendance
+ Timetable
+ Grades
+ Calculated average
+ Messages
+ Appearance & Behavior
+ Languages, themes, subjects sorting
+ App notifications, fix problems
+ Notifications
+ Synchronization
+ Automatic update, synchronization interval
+ Plus and minus values, average calculation
+ Advanced
+ App version, contributors, social portals
+ Displaying advertisements, project support
+
+ New grades
+ New homework
+ New conferences
+ New exams
+ Lucky number
+ New messages
+ New notes
+ New school announcements
+ Push notifications
+ Upcoming lessons
+ Debug
+ Timetable change
+ New attendance
+
+ Black
+ Red
+ Blue
+ Green
+ Purple
+ No color
+
+ Download of updates has started…
+ An update has just been downloaded.
+ Restart
+ Update failed! Wulkanowy may not function properly. Consider updating
+
+ No internet connection
+ An error occurred. Check your device clock
+ Connection to register failed. Servers can be overloaded. Please try again later
+ Loading data failed. Please try again later
+ Register password change required
+ Maintenance underway UONET + register. Try again later
+ Unknown UONET + register error. Try again later
+ Unknown application error. Please try again later
+ An unexpected error occurred
+ Feature disabled by your school
+ Feature not available. Login in a mode other than Mobile API
+ This field is required
+
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index f90e78e7d..2c104ecbf 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -77,9 +77,9 @@
Увійти
Минув термін дії сесії
Минув термін дії сесії, авторизуйтеся знову
- Application support
- Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time
- Enable ads
+ Підтримка додатків
+ Вам подобається цей додаток? Підтримуйте його розвиток, увімкнувши неінвазивну рекламу, яку ви можете будь-коли вимкнути
+ Увімкнути рекламу
Оцінка
%d семестр
@@ -97,7 +97,7 @@
Передбачувана оцінка
Розрахована середня оцінка
Як працює \"Розрахована середня оцінка\"?
- The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages
+ Розрахована середня оцінка - це середнє арифметичне, обчислене з середніх оцінок з предметів.Це дозволяє дізнатися приблизну кінцеву середню оцінку.Вона розраховується способом, обраним користувачем у налаштуваннях програми.Рекомендується вибрати відповідний варіант, тому що кожна школа по різному розраховує середню оцінку.Крім того, якщо у вашій школі повідомляється середня оцінка з предметів на сторінці Vulcan, програма тільки завантажує ці оцінки.Це можна змінити шляхом примусового розрахунку середньоЇ оцінки в налаштуваннях програми.\n\nСередні оцінки тільки за обраний семестр:\n1. Розрахунок середньозваженого числа для кожного предмета в даному семестрі\n2. Сумування розрахованих числ\n3. Розрахунок середнього арифметичного з сумованих чисел\n\nСереднє значення з обох семестрів:\n1. Обчислення середньозваженого числа для кожного предмета у 1 та 2 семестрі\n2. Обчислення середнього арифметичного з розрахованих середньозважених числ за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахованих середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого числа за рік для кожного предмета. Підсумковий середній показник у 1-му семестрі не має значення.\n2. Сумування розрахованих середніх\n3. Обчислення середнього арифметичного з суммованих середніх
Як працює \"Підсумкова середня оцінка\"?
Підсумкове середнє значення - це середнє арифметичне, обчислене з усіх наявних наразі підсумкових оцінок у даному семестрі. \n\nСхема обчислення складається з таких кроків:\n1. Сумування підсумкових оцінок, виставленних викладачами\n2. Ділення на кількість предметів, з яких виставлені ці оцінки
Підсумкова середня оцінка
@@ -297,10 +297,10 @@
Перемістити до кошика
Видалити назавжди
Лист було успішно видалено
- student
- parent
- guardian
- employee
+ студент
+ батькові
+ опікун
+ працівник
Поділитись
Друк
Тема
@@ -720,10 +720,10 @@
Відповісти з історією повідомлень
Вилічити середню аритметичну, якщо оцінка немає вартості
Підтримка
- Privacy Policy
- Agreements
- Consent to processing of data related to ads
- Show ads in app
+ Політика конфіденційності
+ Угоди
+ Згода на обробку даних, пов’язаних з оголошеннями
+ Показувати рекламу в додатку
Подивіться одну рекламу для підтримки проєкту
Згода в обробці даних
Щоб переглянути рекламу, ви повинні погодитися з умовами обробки даних нашої Політики конфіденційності
@@ -731,13 +731,13 @@
Політика конфіденційності
Реклама завантажується
Дякуємо за вашу підтримку, повертайтеся пізніше для більшої кількості реклам
- Can we use your data to display ads?
- You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details
- Personalized ads
- Non-personalized ads
- I am over 18 years old
- Yes, personalized ads
- Yes, non-personalized ads
+ Чи можемо ми використовувати ваші дані для відбивання реклами?
+ Ви можете змінити свій вибір в будь-який час в налаштуваннях додатку. Ми можемо використовувати ваші дані для відбивання оголошень, адаптованої до вас або, використовуючи менше ваших даних, відбивати неперсоналізовані оголошення. Перегляньте нашу Політику конфіденційності для деталей
+ Персоналізовані оголошення
+ Неперсоналізована реклама
+ Мені більше 18 років
+ Так, персоналізована реклама
+ Так, неперсоналізована реклама
Додатково
Вигляд та поведінка
Сповіщення
From 50b6d380b6e7c9c5537ddcce698e9b8c00c9db0d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Wed, 2 Nov 2022 16:44:05 +0100
Subject: [PATCH 262/669] Fix unexpected error in support ad when no internet
(#2030)
---
.../ui/modules/settings/ads/AdsPresenter.kt | 21 ++++++++++++-----
.../io/github/wulkanowy/utils/AdsHelper.kt | 23 +++++++++++++++++++
2 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt
index 772d616d7..28c98e3c3 100644
--- a/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt
+++ b/app/src/play/java/io/github/wulkanowy/ui/modules/settings/ads/AdsPresenter.kt
@@ -31,13 +31,22 @@ class AdsPresenter @Inject constructor(
view?.showLoadingSupportAd(true)
presenterScope.launch {
runCatching { adsHelper.getSupportAd() }
- .onFailure(errorHandler::dispatch)
- .onSuccess { it?.let { view?.showAd(it) } }
+ .onFailure {
+ errorHandler.dispatch(it)
- view?.run {
- showLoadingSupportAd(false)
- showWatchAdOncePerVisit(true)
- }
+ view?.run {
+ showLoadingSupportAd(false)
+ showWatchAdOncePerVisit(false)
+ }
+ }
+ .onSuccess {
+ it?.let { view?.showAd(it) }
+
+ view?.run {
+ showLoadingSupportAd(false)
+ showWatchAdOncePerVisit(true)
+ }
+ }
}
}
diff --git a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt
index c536e2218..d5f65b46d 100644
--- a/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt
+++ b/app/src/play/java/io/github/wulkanowy/utils/AdsHelper.kt
@@ -1,8 +1,12 @@
package io.github.wulkanowy.utils
import android.content.Context
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
+import android.os.Build
import android.os.Bundle
import android.view.View
+import androidx.core.content.getSystemService
import com.google.ads.mediation.admob.AdMobAdapter
import com.google.android.gms.ads.*
import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd
@@ -10,6 +14,7 @@ import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAdLoa
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.BuildConfig
import io.github.wulkanowy.data.repositories.PreferencesRepository
+import java.net.UnknownHostException
import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
@@ -28,6 +33,10 @@ class AdsHelper @Inject constructor(
}
suspend fun getSupportAd(): RewardedInterstitialAd? {
+ if (!context.isInternetConnected()) {
+ throw UnknownHostException()
+ }
+
val extra = Bundle().apply { putString("npa", "1") }
val adRequest = AdRequest.Builder()
.apply {
@@ -84,4 +93,18 @@ class AdsHelper @Inject constructor(
}
}
+@Suppress("DEPRECATION")
+private fun Context.isInternetConnected(): Boolean {
+ val connectivityManager = getSystemService() ?: return false
+
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ val currentNetwork = connectivityManager.activeNetwork
+ val networkCapabilities = connectivityManager.getNetworkCapabilities(currentNetwork)
+
+ networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) == true
+ } else {
+ connectivityManager.activeNetworkInfo?.isConnected == true
+ }
+}
+
data class AdBanner(val view: View)
From 1257dc63d3c081ec75dffebfebf2fdc14dacd18f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Nov 2022 18:21:17 +0000
Subject: [PATCH 263/669] Bump runner from 1.4.0 to 1.5.1 (#2047)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index a8f68884e..7142d92fb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -264,7 +264,7 @@ dependencies {
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testImplementation 'org.robolectric:robolectric:4.9'
- testImplementation "androidx.test:runner:1.4.0"
+ testImplementation "androidx.test:runner:1.5.1"
testImplementation "androidx.test.ext:junit:1.1.3"
testImplementation "androidx.test:core:1.4.0"
testImplementation "androidx.room:room-testing:$room"
From 66ff14f719bf021b83b43e09e5bca726a843d088 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Nov 2022 18:21:35 +0000
Subject: [PATCH 264/669] Bump agcp from 1.7.3.300 to 1.7.3.301 (#2046)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 3773dbc38..ccce1c433 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.14'
- classpath 'com.huawei.agconnect:agcp:1.7.3.300'
+ classpath 'com.huawei.agconnect:agcp:1.7.3.301'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4"
From fded5007c14ecbec95680e5b9c5960cab3e77927 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Nov 2022 18:22:25 +0000
Subject: [PATCH 265/669] Bump firebase-bom from 31.0.2 to 31.0.3 (#2041)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 7142d92fb..40f571714 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
- playImplementation platform('com.google.firebase:firebase-bom:31.0.2')
+ playImplementation platform('com.google.firebase:firebase-bom:31.0.3')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 885319a8854f22cfa91dd097db8492599d09fd25 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Nov 2022 18:36:20 +0000
Subject: [PATCH 266/669] Bump hilt_version from 2.44 to 2.44.1 (#2044)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index ccce1c433..6f3ac4d63 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ buildscript {
ext {
kotlin_version = '1.7.20'
about_libraries = '10.5.1'
- hilt_version = "2.44"
+ hilt_version = "2.44.1"
}
repositories {
mavenCentral()
From d6385e8cdd06b3636568b32aab5c0064e8bbd8cc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Nov 2022 20:31:02 +0000
Subject: [PATCH 267/669] Bump core from 1.4.0 to 1.5.0 (#2045)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 40f571714..1edcc753e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -266,7 +266,7 @@ dependencies {
testImplementation 'org.robolectric:robolectric:4.9'
testImplementation "androidx.test:runner:1.5.1"
testImplementation "androidx.test.ext:junit:1.1.3"
- testImplementation "androidx.test:core:1.4.0"
+ testImplementation "androidx.test:core:1.5.0"
testImplementation "androidx.room:room-testing:$room"
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
From b8296ac02f94e17e972c74980bcf480352abaef6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Nov 2022 20:31:26 +0000
Subject: [PATCH 268/669] Bump kotlin_version from 1.7.20 to 1.7.21 (#2042)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 6f3ac4d63..e8e1052b6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
buildscript {
ext {
- kotlin_version = '1.7.20'
+ kotlin_version = '1.7.21'
about_libraries = '10.5.1'
hilt_version = "2.44.1"
}
From 4d49e956b881125354b86228a12c50e49ea6fd8c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 14 Nov 2022 20:48:12 +0000
Subject: [PATCH 269/669] Bump junit from 1.1.3 to 1.1.4 (#2043)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 1edcc753e..11f055ea8 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -265,7 +265,7 @@ dependencies {
testImplementation 'org.robolectric:robolectric:4.9'
testImplementation "androidx.test:runner:1.5.1"
- testImplementation "androidx.test.ext:junit:1.1.3"
+ testImplementation "androidx.test.ext:junit:1.1.4"
testImplementation "androidx.test:core:1.5.0"
testImplementation "androidx.room:room-testing:$room"
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
From db4f172fb83bd4ca4a3f7219ae5e6f40f87d5727 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Wed, 16 Nov 2022 12:54:55 +0100
Subject: [PATCH 270/669] Fix unread status in sent messages (#2048)
---
app/build.gradle | 2 +-
.../52.json | 2421 +++++++++++++++++
.../github/wulkanowy/data/db/AppDatabase.kt | 3 +-
.../wulkanowy/data/db/entities/Message.kt | 6 +
.../wulkanowy/data/mappers/MessageMapper.kt | 4 +-
.../debug/notification/mock/message.kt | 2 +
.../message/preview/MessagePreviewAdapter.kt | 16 +-
app/src/main/res/values/strings.xml | 1 +
.../repositories/MessageRepositoryTest.kt | 6 +-
9 files changed, 2451 insertions(+), 10 deletions(-)
create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json
diff --git a/app/build.gradle b/app/build.gradle
index 11f055ea8..99672f1f9 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:2840d9d6d0"
+ implementation "io.github.wulkanowy:sdk:701016eda2"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json
new file mode 100644
index 000000000..129d1917b
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json
@@ -0,0 +1,2421 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 52,
+ "identityHash": "8742176f26afcc81279d4a073dca2949",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "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"
+ ],
+ "orders": [],
+ "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}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "kindergartenDiaryId",
+ "columnName": "kindergarten_diary_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "kindergarten_diary_id",
+ "semester_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mailboxKey",
+ "columnName": "mailbox_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondents",
+ "columnName": "correspondents",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "recipients",
+ "columnName": "recipients",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+ "fields": [
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "real_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Mailboxes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `userLoginId` INTEGER NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))",
+ "fields": [
+ {
+ "fieldPath": "globalKey",
+ "columnName": "globalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "userLoginId",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "studentName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolNameShort",
+ "columnName": "schoolNameShort",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "globalKey"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "mailboxGlobalKey",
+ "columnName": "mailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentMailboxGlobalKey",
+ "columnName": "studentMailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "schoolShortName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "destination",
+ "columnName": "destination",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'"
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDismissible",
+ "columnName": "is_dismissible",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "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, '8742176f26afcc81279d4a073dca2949')"
+ ]
+ }
+}
\ No newline at end of file
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 15b38805b..e61ffe844 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
@@ -47,6 +47,7 @@ import javax.inject.Singleton
AutoMigration(from = 44, to = 45),
AutoMigration(from = 46, to = 47),
AutoMigration(from = 47, to = 48),
+ AutoMigration(from = 51, to = 52),
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@@ -55,7 +56,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
- const val VERSION_SCHEMA = 51
+ const val VERSION_SCHEMA = 52
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
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 77874e03d..323b00bec 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,12 @@ data class Message(
var unread: Boolean,
+ @ColumnInfo(name = "read_by")
+ val readBy: Int?,
+
+ @ColumnInfo(name = "unread_by")
+ val unreadBy: Int?,
+
@ColumnInfo(name = "has_attachments")
val hasAttachments: Boolean
) : Serializable {
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
index 2e7967f0e..c329607f4 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
@@ -16,7 +16,9 @@ fun List.mapToEntities(mailbox: Mailbox) = map {
date = it.dateZoned.toInstant(),
folderId = it.folderId,
unread = it.unread,
- hasAttachments = it.hasAttachments
+ unreadBy = it.unreadBy,
+ readBy = it.readBy,
+ hasAttachments = it.hasAttachments,
).apply {
content = it.content.orEmpty()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt
index 6ff26162b..e31bd84a9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt
@@ -22,6 +22,8 @@ private fun generateMessage(sender: String, subject: String) = Message(
date = Instant.now(),
folderId = 0,
unread = true,
+ readBy = 2,
+ unreadBy = 2,
hasAttachments = false,
messageGlobalKey = "",
correspondents = sender,
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 3c1c53d39..d3c6b95c7 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
@@ -1,6 +1,5 @@
package io.github.wulkanowy.ui.modules.message.preview
-import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -74,15 +73,20 @@ class MessagePreviewAdapter @Inject constructor() :
}
}
- @SuppressLint("SetTextI18n")
private fun bindMessage(holder: MessageViewHolder, message: Message) {
val context = holder.binding.root.context
+ val recipientCount = (message.unreadBy ?: 0) + (message.readBy ?: 0)
+ val isReceived = message.unreadBy == null
- val readTextValue = when {
- !message.unread -> R.string.all_yes
- else -> R.string.all_no
+ val readText = when {
+ recipientCount > 1 -> {
+ context.getString(R.string.message_read_by, message.readBy, recipientCount)
+ }
+ message.readBy == 1 || (isReceived && !message.unread) -> {
+ context.getString(R.string.message_read, context.getString(R.string.all_yes))
+ }
+ else -> context.getString(R.string.message_read, context.getString(R.string.all_no))
}
- val readText = context.getString(R.string.message_read, context.getString(readTextValue))
with(holder.binding) {
messagePreviewSubject.text = message.subject.ifBlank {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 48376d216..e65a27fcc 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -304,6 +304,7 @@
Only unread
Only with attachments
Read: %s
+ Read by: %1$d of %2$d people
- %1$d message
- %1$d messages
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
index 4efc9c60a..9fc83a23b 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
@@ -193,7 +193,9 @@ class MessageRepositoryTest {
date = Instant.EPOCH,
folderId = 1,
unread = unread,
- hasAttachments = false
+ readBy = 1,
+ unreadBy = 1,
+ hasAttachments = false,
).apply {
this.content = content
}
@@ -209,6 +211,8 @@ class MessageRepositoryTest {
dateZoned = Instant.EPOCH.atZone(ZoneOffset.UTC),
folderId = 1,
unread = true,
+ readBy = 1,
+ unreadBy = 1,
hasAttachments = false,
)
}
From 51a1097bb4af2373bcd14ac8189e66f1cb606229 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Wed, 16 Nov 2022 13:46:47 +0100
Subject: [PATCH 271/669] Add mailbox chooser to messages (#2002)
---
.../53.json | 2439 +++++++++++++++++
.../github/wulkanowy/data/db/AppDatabase.kt | 3 +-
.../wulkanowy/data/db/dao/MailboxDao.kt | 8 +-
.../wulkanowy/data/db/dao/MessagesDao.kt | 3 +
.../wulkanowy/data/db/entities/Mailbox.kt | 11 +-
.../wulkanowy/data/db/entities/Message.kt | 3 +
.../data/db/migrations/Migration53.kt | 57 +
.../wulkanowy/data/mappers/MailboxMapper.kt | 4 +-
.../wulkanowy/data/mappers/MessageMapper.kt | 7 +-
.../data/repositories/MailboxRepository.kt | 85 -
.../data/repositories/MessageRepository.kt | 67 +-
.../data/repositories/RecipientRepository.kt | 24 +-
.../messages/GetMailboxByStudentUseCase.kt | 52 +
.../notifications/NewMessageNotification.kt | 1 -
.../services/sync/works/MessageWork.kt | 6 +-
.../services/sync/works/RecipientWork.kt | 15 +-
.../modules/dashboard/DashboardPresenter.kt | 3 +-
.../debug/notification/mock/message.kt | 1 +
.../mailboxchooser/MailboxChooserAdapter.kt | 81 +
.../mailboxchooser/MailboxChooserDialog.kt | 75 +
.../mailboxchooser/MailboxChooserItem.kt | 9 +
.../mailboxchooser/MailboxChooserPresenter.kt | 38 +
.../mailboxchooser/MailboxChooserView.kt | 13 +
.../preview/MessagePreviewPresenter.kt | 6 +-
.../message/send/SendMessageActivity.kt | 16 +
.../message/send/SendMessagePresenter.kt | 107 +-
.../modules/message/send/SendMessageView.kt | 1 +
.../modules/message/tab/MessageTabAdapter.kt | 32 +-
.../modules/message/tab/MessageTabDataItem.kt | 1 +
.../modules/message/tab/MessageTabFragment.kt | 20 +
.../message/tab/MessageTabPresenter.kt | 39 +-
.../ui/modules/message/tab/MessageTabView.kt | 3 +
.../io/github/wulkanowy/utils/RefreshUtils.kt | 5 +-
.../main/res/layout/activity_send_message.xml | 20 +-
.../res/layout/dialog_mailbox_chooser.xml | 40 +
.../main/res/layout/item_mailbox_chooser.xml | 43 +
.../main/res/layout/item_message_chips.xml | 23 +-
app/src/main/res/values/strings.xml | 2 +
.../io/github/wulkanowy/TestEnityCreator.kt | 4 +-
.../repositories/MessageRepositoryTest.kt | 24 +-
.../GetMailboxByStudentUseCaseTest.kt} | 53 +-
41 files changed, 3209 insertions(+), 235 deletions(-)
create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json
create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt
delete mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt
create mode 100644 app/src/main/res/layout/dialog_mailbox_chooser.xml
create mode 100644 app/src/main/res/layout/item_mailbox_chooser.xml
rename app/src/test/java/io/github/wulkanowy/{data/repositories/MailboxRepositoryTest.kt => domain/GetMailboxByStudentUseCaseTest.kt} (77%)
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json
new file mode 100644
index 000000000..985617872
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json
@@ -0,0 +1,2439 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 53,
+ "identityHash": "1dc96a366125ec9f8567da87cdc9c863",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "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"
+ ],
+ "orders": [],
+ "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}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "kindergartenDiaryId",
+ "columnName": "kindergarten_diary_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "kindergarten_diary_id",
+ "semester_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mailboxKey",
+ "columnName": "mailbox_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondents",
+ "columnName": "correspondents",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "recipients",
+ "columnName": "recipients",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+ "fields": [
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "real_id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "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
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Mailboxes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))",
+ "fields": [
+ {
+ "fieldPath": "globalKey",
+ "columnName": "globalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolId",
+ "columnName": "schoolId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "studentName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolNameShort",
+ "columnName": "schoolNameShort",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "globalKey"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "mailboxGlobalKey",
+ "columnName": "mailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentMailboxGlobalKey",
+ "columnName": "studentMailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "schoolShortName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`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, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "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": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "destination",
+ "columnName": "destination",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'"
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": true
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `type` TEXT NOT NULL, `is_dismissible` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDismissible",
+ "columnName": "is_dismissible",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "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, '1dc96a366125ec9f8567da87cdc9c863')"
+ ]
+ }
+}
\ No newline at end of file
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 e61ffe844..792611a81 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
@@ -56,7 +56,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
- const val VERSION_SCHEMA = 52
+ const val VERSION_SCHEMA = 53
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
@@ -106,6 +106,7 @@ abstract class AppDatabase : RoomDatabase() {
Migration49(),
Migration50(),
Migration51(),
+ Migration53(),
)
fun newInstance(
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt
index c44ecd0c2..084192a07 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MailboxDao.kt
@@ -3,12 +3,16 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Mailbox
+import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@Dao
interface MailboxDao : BaseDao {
- @Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId ")
- suspend fun loadAll(userLoginId: Int): List
+ @Query("SELECT * FROM Mailboxes WHERE email = :email")
+ suspend fun loadAll(email: String): List
+
+ @Query("SELECT * FROM Mailboxes WHERE email = :email AND symbol = :symbol AND schoolId = :schoolId")
+ fun loadAll(email: String, symbol: String, schoolId: String): Flow>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt
index 8c730c9bc..1709f7636 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt
@@ -16,4 +16,7 @@ interface MessagesDao : BaseDao {
@Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
fun loadAll(mailboxKey: String, folder: Int): Flow>
+
+ @Query("SELECT * FROM Messages WHERE email = :email AND folder_id = :folder ORDER BY date DESC")
+ fun loadAll(folder: Int, email: String): Flow>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt
index 7c08e481d..e65e213dd 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Mailbox.kt
@@ -1,20 +1,27 @@
package io.github.wulkanowy.data.db.entities
+import android.os.Parcelable
import androidx.room.Entity
import androidx.room.PrimaryKey
+import kotlinx.parcelize.Parcelize
+@Parcelize
@Entity(tableName = "Mailboxes")
data class Mailbox(
@PrimaryKey
val globalKey: String,
+
+ val email: String,
+ val symbol: String,
+ val schoolId: String,
+
val fullName: String,
val userName: String,
- val userLoginId: Int,
val studentName: String,
val schoolNameShort: String,
val type: MailboxType,
-)
+) : java.io.Serializable, Parcelable
enum class MailboxType {
STUDENT,
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 323b00bec..d1356b33d 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
@@ -9,6 +9,9 @@ import java.time.Instant
@Entity(tableName = "Messages")
data class Message(
+ @ColumnInfo(name = "email")
+ val email: String,
+
@ColumnInfo(name = "message_global_key")
val messageGlobalKey: String,
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt
new file mode 100644
index 000000000..12624a51a
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration53.kt
@@ -0,0 +1,57 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration53 : Migration(52, 53) {
+
+ override fun migrate(database: SupportSQLiteDatabase) {
+ createMailboxTable(database)
+ recreateMessagesTable(database)
+ }
+
+ private fun createMailboxTable(database: SupportSQLiteDatabase) {
+ database.execSQL("DROP TABLE IF EXISTS Mailboxes")
+ database.execSQL(
+ """
+ CREATE TABLE IF NOT EXISTS `Mailboxes` (
+ `globalKey` TEXT NOT NULL,
+ `email` TEXT NOT NULL,
+ `symbol` TEXT NOT NULL,
+ `schoolId` TEXT NOT NULL,
+ `fullName` TEXT NOT NULL,
+ `userName` TEXT NOT NULL,
+ `studentName` TEXT NOT NULL,
+ `schoolNameShort` TEXT NOT NULL,
+ `type` TEXT NOT NULL,
+ PRIMARY KEY(`globalKey`)
+ )""".trimIndent()
+ )
+ }
+
+ private fun recreateMessagesTable(database: SupportSQLiteDatabase) {
+ database.execSQL("DROP TABLE IF EXISTS Messages")
+ database.execSQL(
+ """
+ CREATE TABLE IF NOT EXISTS `Messages` (
+ `email` TEXT NOT NULL,
+ `message_global_key` TEXT NOT NULL,
+ `mailbox_key` TEXT NOT NULL,
+ `message_id` INTEGER NOT NULL,
+ `correspondents` TEXT NOT NULL,
+ `subject` TEXT NOT NULL,
+ `date` INTEGER NOT NULL,
+ `folder_id` INTEGER NOT NULL,
+ `unread` INTEGER NOT NULL,
+ `read_by` INTEGER,
+ `unread_by` INTEGER,
+ `has_attachments` INTEGER NOT NULL,
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+ `is_notified` INTEGER NOT NULL,
+ `content` TEXT NOT NULL,
+ `sender` TEXT,
+ `recipients` TEXT
+ )""".trimIndent()
+ )
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt
index 2ccca1b90..0cf547770 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MailboxMapper.kt
@@ -10,9 +10,11 @@ fun List.mapToEntities(student: Student) = map {
globalKey = it.globalKey,
fullName = it.fullName,
userName = it.userName,
- userLoginId = student.userLoginId,
studentName = it.studentName,
schoolNameShort = it.schoolNameShort,
type = MailboxType.valueOf(it.type.name),
+ email = student.email,
+ symbol = student.symbol,
+ schoolId = student.schoolSymbol,
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
index c329607f4..8825c5744 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
@@ -6,10 +6,13 @@ import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
-fun List.mapToEntities(mailbox: Mailbox) = map {
+fun List.mapToEntities(student: Student, mailbox: Mailbox?, allMailboxes: List) = map {
Message(
messageGlobalKey = it.globalKey,
- mailboxKey = mailbox.globalKey,
+ mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box ->
+ box.fullName == it.mailbox
+ }?.globalKey!!,
+ email = student.email,
messageId = it.id,
correspondents = it.correspondents,
subject = it.subject.trim(),
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
deleted file mode 100644
index c571937ad..000000000
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MailboxRepository.kt
+++ /dev/null
@@ -1,85 +0,0 @@
-package io.github.wulkanowy.data.repositories
-
-import io.github.wulkanowy.data.db.dao.MailboxDao
-import io.github.wulkanowy.data.db.entities.Mailbox
-import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.mappers.mapToEntities
-import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.github.wulkanowy.utils.getRefreshKey
-import io.github.wulkanowy.utils.init
-import io.github.wulkanowy.utils.uniqueSubtract
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class MailboxRepository @Inject constructor(
- private val mailboxDao: MailboxDao,
- private val sdk: Sdk,
- private val refreshHelper: AutoRefreshHelper,
-) {
- private val cacheKey = "mailboxes"
-
- suspend fun refreshMailboxes(student: Student) {
- val new = sdk.init(student).getMailboxes().mapToEntities(student)
- val old = mailboxDao.loadAll(student.userLoginId)
-
- mailboxDao.deleteAll(old uniqueSubtract new)
- mailboxDao.insertAll(new uniqueSubtract old)
-
- refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
- }
-
- suspend fun getMailbox(student: Student): Mailbox {
- val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
- val mailboxes = mailboxDao.loadAll(student.userLoginId)
- val mailbox = mailboxes.filterByStudent(student)
-
- return if (isExpired || mailbox == null) {
- refreshMailboxes(student)
- val newMailbox = mailboxDao.loadAll(student.userLoginId).filterByStudent(student)
-
- requireNotNull(newMailbox) {
- "Mailbox for ${student.userName} - ${student.studentName} not found! Saved mailboxes: $mailboxes"
- }
-
- newMailbox
- } else mailbox
- }
-
- private fun List.filterByStudent(student: Student): Mailbox? {
- val normalizedStudentName = student.studentName.normalizeStudentName()
-
- return find {
- it.studentName.normalizeStudentName() == normalizedStudentName
- } ?: singleOrNull {
- it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart()
- } ?: singleOrNull {
- it.studentName.getUnauthorizedVersion() == normalizedStudentName
- }
- }
-
- private fun String.normalizeStudentName(): String {
- return trim().split(" ")
- .filter { it.isNotBlank() }
- .joinToString(" ") { part ->
- part.lowercase().replaceFirstChar { it.uppercase() }
- }
- }
-
- private fun String.getFirstAndLastPart(): String {
- val parts = normalizeStudentName().split(" ")
-
- val endParts = parts.filterIndexed { i, _ ->
- i == 0 || parts.size - 1 == i
- }
- return endParts.joinToString(" ")
- }
-
- private fun String.getUnauthorizedVersion(): String {
- return normalizeStudentName().split(" ")
- .joinToString(" ") {
- it.first() + "*".repeat(it.length - 1)
- }
- }
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
index e7428762e..f8be4296d 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
@@ -5,6 +5,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.db.SharedPrefProvider
+import io.github.wulkanowy.data.db.dao.MailboxDao
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
import io.github.wulkanowy.data.db.dao.MessagesDao
import io.github.wulkanowy.data.db.entities.*
@@ -15,6 +16,8 @@ import io.github.wulkanowy.data.mappers.mapFromEntities
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.data.pojos.MessageDraft
+import io.github.wulkanowy.data.toFirstResult
+import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Folder
import io.github.wulkanowy.utils.AutoRefreshHelper
@@ -40,16 +43,18 @@ class MessageRepository @Inject constructor(
private val refreshHelper: AutoRefreshHelper,
private val sharedPrefProvider: SharedPrefProvider,
private val json: Json,
+ private val mailboxDao: MailboxDao,
+ private val getMailboxByStudentUseCase: GetMailboxByStudentUseCase,
) {
private val saveFetchResultMutex = Mutex()
- private val cacheKey = "message"
+ private val messagesCacheKey = "message"
+ private val mailboxCacheKey = "mailboxes"
- @Suppress("UNUSED_PARAMETER")
fun getMessages(
student: Student,
- mailbox: Mailbox,
+ mailbox: Mailbox?,
folder: MessageFolder,
forceRefresh: Boolean,
notify: Boolean = false,
@@ -58,16 +63,20 @@ class MessageRepository @Inject constructor(
isResultEmpty = { it.isEmpty() },
shouldFetch = {
val isExpired = refreshHelper.shouldBeRefreshed(
- key = getRefreshKey(cacheKey, student, folder)
+ key = getRefreshKey(messagesCacheKey, mailbox, folder)
)
it.isEmpty() || forceRefresh || isExpired
},
- query = { messagesDb.loadAll(mailbox.globalKey, folder.id) },
+ query = {
+ if (mailbox == null) {
+ messagesDb.loadAll(folder.id, student.email)
+ } else messagesDb.loadAll(mailbox.globalKey, folder.id)
+ },
fetch = {
sdk.init(student).getMessages(
folder = Folder.valueOf(folder.name),
- mailboxKey = mailbox.globalKey,
- ).mapToEntities(mailbox)
+ mailboxKey = mailbox?.globalKey,
+ ).mapToEntities(student, mailbox, mailboxDao.loadAll(student.email))
},
saveFetchResult = { old, new ->
messagesDb.deleteAll(old uniqueSubtract new)
@@ -75,7 +84,9 @@ class MessageRepository @Inject constructor(
it.isNotified = !notify
})
- refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder))
+ refreshHelper.updateLastRefreshTimestamp(
+ getRefreshKey(messagesCacheKey, mailbox, folder)
+ )
}
)
@@ -90,7 +101,9 @@ class MessageRepository @Inject constructor(
Timber.d("Message content in db empty: ${it.message.content.isBlank()}")
it.message.unread || it.message.content.isBlank()
},
- query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) },
+ query = {
+ messagesDb.loadMessageWithAttachment(message.messageGlobalKey)
+ },
fetch = {
sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey, markAsRead)
},
@@ -113,8 +126,10 @@ class MessageRepository @Inject constructor(
}
)
- fun getMessagesFromDatabase(mailbox: Mailbox): Flow> {
- return messagesDb.loadAll(mailbox.globalKey, RECEIVED.id)
+ fun getMessagesFromDatabase(student: Student, mailbox: Mailbox?): Flow> {
+ return if (mailbox == null) {
+ messagesDb.loadAll(RECEIVED.id, student.email)
+ } else messagesDb.loadAll(mailbox.globalKey, RECEIVED.id)
}
suspend fun updateMessages(messages: List) {
@@ -136,7 +151,7 @@ class MessageRepository @Inject constructor(
)
}
- suspend fun deleteMessages(student: Student, mailbox: Mailbox, messages: List) {
+ suspend fun deleteMessages(student: Student, mailbox: Mailbox?, messages: List) {
val firstMessage = messages.first()
sdk.init(student).deleteMessages(
messages = messages.map { it.messageGlobalKey },
@@ -169,6 +184,34 @@ class MessageRepository @Inject constructor(
deleteMessages(student, mailbox, listOf(message))
}
+ suspend fun getMailboxes(student: Student, forceRefresh: Boolean) = networkBoundResource(
+ mutex = saveFetchResultMutex,
+ isResultEmpty = { it.isEmpty() },
+ shouldFetch = {
+ val isExpired = refreshHelper.shouldBeRefreshed(
+ key = getRefreshKey(mailboxCacheKey, student),
+ )
+ it.isEmpty() || isExpired || forceRefresh
+ },
+ query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) },
+ fetch = { sdk.init(student).getMailboxes().mapToEntities(student) },
+ saveFetchResult = { old, new ->
+ mailboxDao.deleteAll(old uniqueSubtract new)
+ mailboxDao.insertAll(new uniqueSubtract old)
+
+ refreshHelper.updateLastRefreshTimestamp(getRefreshKey(mailboxCacheKey, student))
+ }
+ )
+
+ suspend fun getMailboxByStudent(student: Student): Mailbox? {
+ val mailbox = getMailboxByStudentUseCase(student)
+
+ return if (mailbox == null) {
+ getMailboxes(student, forceRefresh = true).toFirstResult()
+ getMailboxByStudentUseCase(student)
+ } else mailbox
+ }
+
var draftMessage: MessageDraft?
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_draft))
?.let { json.decodeFromString(it) }
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt
index e80f028e1..79984ce6d 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt
@@ -33,9 +33,11 @@ class RecipientRepository @Inject constructor(
suspend fun getRecipients(
student: Student,
- mailbox: Mailbox,
- type: MailboxType
+ mailbox: Mailbox?,
+ type: MailboxType,
): List {
+ mailbox ?: return emptyList()
+
val cached = recipientDb.loadAll(type, mailbox.globalKey)
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
@@ -47,11 +49,15 @@ class RecipientRepository @Inject constructor(
suspend fun getMessageSender(
student: Student,
- mailbox: Mailbox,
- message: Message
- ): List = sdk.init(student)
- .getMessageReplayDetails(message.messageGlobalKey)
- .sender
- .let(::listOf)
- .mapToEntities(mailbox.globalKey)
+ mailbox: Mailbox?,
+ message: Message,
+ ): List {
+ mailbox ?: return emptyList()
+
+ return sdk.init(student)
+ .getMessageReplayDetails(message.messageGlobalKey)
+ .sender
+ .let(::listOf)
+ .mapToEntities(mailbox.globalKey)
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt
new file mode 100644
index 000000000..d96794e51
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt
@@ -0,0 +1,52 @@
+package io.github.wulkanowy.domain.messages
+
+import io.github.wulkanowy.data.db.dao.MailboxDao
+import io.github.wulkanowy.data.db.entities.Mailbox
+import io.github.wulkanowy.data.db.entities.Student
+import javax.inject.Inject
+
+class GetMailboxByStudentUseCase @Inject constructor(
+ private val mailboxDao: MailboxDao,
+) {
+
+ suspend operator fun invoke(student: Student): Mailbox? {
+ return mailboxDao.loadAll(student.email)
+ .filterByStudent(student)
+ }
+
+ private fun List.filterByStudent(student: Student): Mailbox? {
+ val normalizedStudentName = student.studentName.normalizeStudentName()
+
+ return find {
+ it.studentName.normalizeStudentName() == normalizedStudentName
+ } ?: singleOrNull {
+ it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart()
+ } ?: singleOrNull {
+ it.studentName.getUnauthorizedVersion() == normalizedStudentName
+ }
+ }
+
+ private fun String.normalizeStudentName(): String {
+ return trim().split(" ")
+ .filter { it.isNotBlank() }
+ .joinToString(" ") { part ->
+ part.lowercase().replaceFirstChar { it.uppercase() }
+ }
+ }
+
+ private fun String.getFirstAndLastPart(): String {
+ val parts = normalizeStudentName().split(" ")
+
+ val endParts = parts.filterIndexed { i, _ ->
+ i == 0 || parts.size - 1 == i
+ }
+ return endParts.joinToString(" ")
+ }
+
+ private fun String.getUnauthorizedVersion(): String {
+ return normalizeStudentName().split(" ")
+ .joinToString(" ") {
+ it.first() + "*".repeat(it.length - 1)
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt
index 3b7bcff05..45523d51e 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/notifications/NewMessageNotification.kt
@@ -8,7 +8,6 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.GroupNotificationData
import io.github.wulkanowy.data.pojos.NotificationData
import io.github.wulkanowy.ui.modules.Destination
-import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.getPlural
import javax.inject.Inject
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 180568267..c7824e61f 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
@@ -3,7 +3,6 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
-import io.github.wulkanowy.data.repositories.MailboxRepository
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.NewMessageNotification
@@ -12,12 +11,11 @@ import javax.inject.Inject
class MessageWork @Inject constructor(
private val messageRepository: MessageRepository,
- private val mailboxRepository: MailboxRepository,
private val newMessageNotification: NewMessageNotification,
) : Work {
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
- val mailbox = mailboxRepository.getMailbox(student)
+ val mailbox = messageRepository.getMailboxByStudent(student)
messageRepository.getMessages(
student = student,
mailbox = mailbox,
@@ -26,7 +24,7 @@ class MessageWork @Inject constructor(
notify = notify
).waitForResult()
- messageRepository.getMessagesFromDatabase(mailbox).first()
+ messageRepository.getMessagesFromDatabase(student, mailbox).first()
.filter { !it.isNotified && it.unread }.let {
if (it.isNotEmpty()) newMessageNotification.notify(it, student)
messageRepository.updateMessages(it.onEach { message -> message.isNotified = true })
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt
index b1322ada3..90b20651d 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/RecipientWork.kt
@@ -1,22 +1,23 @@
package io.github.wulkanowy.services.sync.works
+import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.repositories.MailboxRepository
+import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.repositories.RecipientRepository
+import io.github.wulkanowy.data.toFirstResult
import javax.inject.Inject
class RecipientWork @Inject constructor(
- private val mailboxRepository: MailboxRepository,
+ private val messageRepository: MessageRepository,
private val recipientRepository: RecipientRepository
) : Work {
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
- mailboxRepository.refreshMailboxes(student)
-
- val mailbox = mailboxRepository.getMailbox(student)
-
- recipientRepository.refreshRecipients(student, mailbox, MailboxType.EMPLOYEE)
+ val mailboxes = messageRepository.getMailboxes(student, forceRefresh = true).toFirstResult()
+ mailboxes.dataOrNull?.forEach {
+ recipientRepository.refreshRecipients(student, it, MailboxType.EMPLOYEE)
+ }
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index 350300937..cb92b0043 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -25,7 +25,6 @@ class DashboardPresenter @Inject constructor(
private val gradeRepository: GradeRepository,
private val semesterRepository: SemesterRepository,
private val messageRepository: MessageRepository,
- private val mailboxRepository: MailboxRepository,
private val attendanceSummaryRepository: AttendanceSummaryRepository,
private val timetableRepository: TimetableRepository,
private val homeworkRepository: HomeworkRepository,
@@ -228,7 +227,7 @@ class DashboardPresenter @Inject constructor(
private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) {
flow {
val semester = semesterRepository.getCurrentSemester(student)
- val mailbox = mailboxRepository.getMailbox(student)
+ val mailbox = messageRepository.getMailboxByStudent(student)
val selectedTiles = preferencesRepository.selectedDashboardTiles
val flowSuccess = flowOf(Resource.Success(null))
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt
index e31bd84a9..27d8613a3 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/notification/mock/message.kt
@@ -19,6 +19,7 @@ val debugMessageItems = listOf(
private fun generateMessage(sender: String, subject: String) = Message(
subject = subject,
messageId = 123,
+ email = "",
date = Instant.now(),
folderId = 0,
unread = true,
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt
new file mode 100644
index 000000000..59f6d288d
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserAdapter.kt
@@ -0,0 +1,81 @@
+package io.github.wulkanowy.ui.modules.message.mailboxchooser
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.DiffUtil.ItemCallback
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.Mailbox
+import io.github.wulkanowy.data.db.entities.MailboxType
+import io.github.wulkanowy.databinding.ItemMailboxChooserBinding
+import javax.inject.Inject
+
+class MailboxChooserAdapter @Inject constructor() :
+ ListAdapter(Differ) {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
+ ItemMailboxChooserBinding.inflate(
+ LayoutInflater.from(parent.context), parent, false
+ )
+ )
+
+ override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
+ holder.bind(getItem(position))
+ }
+
+ class ItemViewHolder(
+ private val binding: ItemMailboxChooserBinding,
+ ) : RecyclerView.ViewHolder(binding.root) {
+
+ fun bind(item: MailboxChooserItem) {
+ with(binding) {
+ mailboxItemName.text = item.mailbox?.getFirstLine()
+ ?: root.resources.getString(R.string.message_chip_all_mailboxes)
+ mailboxItemSchool.text = item.mailbox?.getSecondLine()
+ mailboxItemSchool.isVisible = !item.isAll
+
+ root.setOnClickListener { item.onClickListener(item.mailbox) }
+ }
+ }
+
+ private fun Mailbox.getFirstLine() = buildString {
+ if (studentName.isNotBlank() && studentName != userName) {
+ append(studentName)
+ append(" - ")
+ }
+ append(userName)
+ }
+
+ private fun Mailbox.getSecondLine() = buildString {
+ append(schoolNameShort)
+ append(" - ")
+ append(getMailboxType(type))
+ }
+
+ private fun getMailboxType(type: MailboxType): String = when (type) {
+ MailboxType.STUDENT -> R.string.message_mailbox_type_student
+ MailboxType.PARENT -> R.string.message_mailbox_type_parent
+ MailboxType.GUARDIAN -> R.string.message_mailbox_type_guardian
+ MailboxType.EMPLOYEE -> R.string.message_mailbox_type_employee
+ MailboxType.UNKNOWN -> null
+ }.let { it?.let { it1 -> binding.root.resources.getString(it1) }.orEmpty() }
+ }
+
+ private object Differ : ItemCallback() {
+ override fun areItemsTheSame(
+ oldItem: MailboxChooserItem,
+ newItem: MailboxChooserItem
+ ): Boolean {
+ return oldItem.mailbox?.globalKey == newItem.mailbox?.globalKey
+ }
+
+ override fun areContentsTheSame(
+ oldItem: MailboxChooserItem,
+ newItem: MailboxChooserItem
+ ): Boolean {
+ return oldItem == newItem
+ }
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt
new file mode 100644
index 000000000..222412ef1
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt
@@ -0,0 +1,75 @@
+package io.github.wulkanowy.ui.modules.message.mailboxchooser
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.os.bundleOf
+import androidx.fragment.app.setFragmentResult
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.data.db.entities.Mailbox
+import io.github.wulkanowy.databinding.DialogMailboxChooserBinding
+import io.github.wulkanowy.ui.base.BaseDialogFragment
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class MailboxChooserDialog : BaseDialogFragment(), MailboxChooserView {
+
+ @Inject
+ lateinit var presenter: MailboxChooserPresenter
+
+ @Inject
+ lateinit var mailboxAdapter: MailboxChooserAdapter
+
+ companion object {
+ const val LISTENER_KEY = "mailbox_selected"
+ const val MAILBOX_KEY = "selected_mailbox"
+ const val REQUIRED_KEY = "is_mailbox_required"
+
+ fun newInstance(mailboxes: List, isMailboxRequired: Boolean, folder: String) =
+ MailboxChooserDialog().apply {
+ arguments = bundleOf(
+ MAILBOX_KEY to mailboxes.toTypedArray(),
+ REQUIRED_KEY to isMailboxRequired,
+ LISTENER_KEY to folder,
+ )
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setStyle(STYLE_NO_TITLE, 0)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ) = DialogMailboxChooserBinding.inflate(inflater).apply { binding = this }.root
+
+ @Suppress("UNCHECKED_CAST")
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ presenter.onAttachView(
+ view = this,
+ requireMailbox = requireArguments().getBoolean(REQUIRED_KEY, false),
+ mailboxes = requireArguments().getParcelableArray(MAILBOX_KEY).orEmpty()
+ .toList() as List,
+ )
+ }
+
+ override fun initView() {
+ binding.accountQuickDialogRecycler.adapter = mailboxAdapter
+ }
+
+ override fun submitData(items: List) {
+ mailboxAdapter.submitList(items)
+ }
+
+ override fun onMailboxSelected(item: Mailbox?) {
+ setFragmentResult(
+ requestKey = requireArguments().getString(LISTENER_KEY).orEmpty(),
+ result = bundleOf(MAILBOX_KEY to item),
+ )
+ dismiss()
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt
new file mode 100644
index 000000000..6923cf085
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserItem.kt
@@ -0,0 +1,9 @@
+package io.github.wulkanowy.ui.modules.message.mailboxchooser
+
+import io.github.wulkanowy.data.db.entities.Mailbox
+
+data class MailboxChooserItem(
+ val mailbox: Mailbox? = null,
+ val isAll: Boolean = false,
+ val onClickListener: (Mailbox?) -> Unit,
+)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt
new file mode 100644
index 000000000..5bd7c84ab
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserPresenter.kt
@@ -0,0 +1,38 @@
+package io.github.wulkanowy.ui.modules.message.mailboxchooser
+
+import io.github.wulkanowy.data.db.entities.Mailbox
+import io.github.wulkanowy.data.repositories.StudentRepository
+import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.base.ErrorHandler
+import timber.log.Timber
+import javax.inject.Inject
+
+class MailboxChooserPresenter @Inject constructor(
+ errorHandler: ErrorHandler,
+ studentRepository: StudentRepository
+) : BasePresenter(errorHandler, studentRepository) {
+
+ fun onAttachView(view: MailboxChooserView, mailboxes: List, requireMailbox: Boolean) {
+ super.onAttachView(view)
+
+ view.initView()
+ Timber.i("Mailbox chooser view was initialized")
+ view.submitData(getMailboxItems(mailboxes, requireMailbox))
+ }
+
+ private fun getMailboxItems(
+ mailboxes: List,
+ requireMailbox: Boolean,
+ ): List = buildList {
+ if (!requireMailbox) {
+ add(MailboxChooserItem(isAll = true, onClickListener = ::onMailboxSelect))
+ }
+ addAll(mailboxes.map {
+ MailboxChooserItem(mailbox = it, isAll = false, onClickListener = ::onMailboxSelect)
+ })
+ }
+
+ fun onMailboxSelect(item: Mailbox?) {
+ view?.onMailboxSelected(item)
+ }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt
new file mode 100644
index 000000000..2e20ee815
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserView.kt
@@ -0,0 +1,13 @@
+package io.github.wulkanowy.ui.modules.message.mailboxchooser
+
+import io.github.wulkanowy.data.db.entities.Mailbox
+import io.github.wulkanowy.ui.base.BaseView
+
+interface MailboxChooserView : BaseView {
+
+ fun initView()
+
+ fun submitData(items: List)
+
+ fun onMailboxSelected(item: Mailbox?)
+}
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 c011f41f3..fd75f6f3a 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
@@ -6,7 +6,6 @@ import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.enums.MessageFolder
-import io.github.wulkanowy.data.repositories.MailboxRepository
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
@@ -21,7 +20,6 @@ class MessagePreviewPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val messageRepository: MessageRepository,
- private val mailboxRepository: MailboxRepository,
private val analytics: AnalyticsHelper
) : BasePresenter(errorHandler, studentRepository) {
@@ -187,8 +185,8 @@ class MessagePreviewPresenter @Inject constructor(
presenterScope.launch {
runCatching {
val student = studentRepository.getCurrentStudent(decryptPass = true)
- val mailbox = mailboxRepository.getMailbox(student)
- messageRepository.deleteMessage(student, mailbox, message!!)
+ val mailbox = messageRepository.getMailboxByStudent(student)
+ messageRepository.deleteMessage(student, mailbox!!, message!!)
}
.onFailure {
retryCallback = { onMessageDelete() }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt
index 334e389e2..b5f687bd4 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageActivity.kt
@@ -19,9 +19,13 @@ import androidx.core.text.toHtml
import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.databinding.ActivitySendMessageBinding
import io.github.wulkanowy.ui.base.BaseActivity
+import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog
+import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.MAILBOX_KEY
+import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog.Companion.LISTENER_KEY
import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.showSoftInput
@@ -100,6 +104,7 @@ class SendMessageActivity : BaseActivity
+ presenter.onMailboxSelected(bundle.getSerializable(MAILBOX_KEY) as? Mailbox)
+ }
}
@SuppressLint("ClickableViewAccessibility")
@@ -205,6 +213,14 @@ class SendMessageActivity : BaseActivity) {
+ MailboxChooserDialog.newInstance(
+ mailboxes = mailboxes,
+ isMailboxRequired = true,
+ folder = LISTENER_KEY,
+ ).show(supportFragmentManager, "chooser")
+ }
+
override fun popView() {
finish()
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt
index 6c07ee6ad..5ab8f8fc9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt
@@ -1,15 +1,15 @@
package io.github.wulkanowy.ui.modules.message.send
-import io.github.wulkanowy.data.Resource
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient
-import io.github.wulkanowy.data.logResourceStatus
-import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.pojos.MessageDraft
-import io.github.wulkanowy.data.repositories.*
-import io.github.wulkanowy.data.resourceFlow
+import io.github.wulkanowy.data.repositories.MessageRepository
+import io.github.wulkanowy.data.repositories.PreferencesRepository
+import io.github.wulkanowy.data.repositories.RecipientRepository
+import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
@@ -28,7 +28,6 @@ class SendMessagePresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val messageRepository: MessageRepository,
- private val mailboxRepository: MailboxRepository,
private val recipientRepository: RecipientRepository,
private val preferencesRepository: PreferencesRepository,
private val analytics: AnalyticsHelper
@@ -36,10 +35,19 @@ class SendMessagePresenter @Inject constructor(
private val messageUpdateChannel = Channel()
+ private var message: Message? = null
+ private var isReplay: Boolean? = null
+
+ private var mailboxes: List = emptyList()
+ private var selectedMailbox: Mailbox? = null
+
fun onAttachView(view: SendMessageView, reason: String?, message: Message?, reply: Boolean?) {
super.onAttachView(view)
view.initView()
initializeSubjectStream()
+ this.message = message
+ this.isReplay = reply
+
Timber.i("Send message view was initialized")
loadData(message, reply)
with(view) {
@@ -110,16 +118,31 @@ class SendMessagePresenter @Inject constructor(
return false
}
+ fun onOpenMailboxChooser() {
+ view?.showMailboxChooser(mailboxes)
+ }
+
+ fun onMailboxSelected(mailbox: Mailbox?) {
+ selectedMailbox = mailbox
+
+ loadData(message, isReplay)
+ }
+
private fun loadData(message: Message?, reply: Boolean?) {
resourceFlow {
val student = studentRepository.getCurrentStudent()
- val mailbox = mailboxRepository.getMailbox(student)
+
+ if (selectedMailbox == null && mailboxes.isEmpty()) {
+ selectedMailbox = messageRepository.getMailboxByStudent(student)
+ mailboxes = messageRepository.getMailboxes(student, false).toFirstResult()
+ .dataOrNull.orEmpty()
+ }
Timber.i("Loading recipients started")
val recipients = createChips(
recipients = recipientRepository.getRecipients(
student = student,
- mailbox = mailbox,
+ mailbox = selectedMailbox,
type = MailboxType.EMPLOYEE,
)
)
@@ -130,7 +153,7 @@ class SendMessagePresenter @Inject constructor(
message != null && reply == true -> recipientRepository.getMessageSender(
student = student,
message = message,
- mailbox = mailbox,
+ mailbox = selectedMailbox,
)
else -> emptyList()
}.let { createChips(it) }
@@ -139,39 +162,42 @@ class SendMessagePresenter @Inject constructor(
messageRecipients.size
)
- Triple(mailbox, recipients, messageRecipients)
+ recipients to messageRecipients
}
.logResourceStatus("load recipients")
- .onEach {
- when (it) {
- is Resource.Loading -> view?.run {
- showProgress(true)
- showContent(false)
- }
- is Resource.Success -> it.data.let { (mailbox, recipientChips, selectedRecipientChips) ->
- view?.run {
- setMailbox(getMailboxName(mailbox))
- setRecipients(recipientChips)
- if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(
- selectedRecipientChips
- )
- showContent(true)
- }
- }
- is Resource.Error -> {
- view?.showContent(true)
- errorHandler.dispatch(it.error)
+ .onResourceLoading {
+ view?.run {
+ showProgress(true)
+ showContent(false)
+ }
+ }
+ .onResourceNotLoading {
+ view?.run { showProgress(false) }
+ }
+ .onResourceError {
+ view?.showContent(true)
+ errorHandler.dispatch(it)
+ }
+ .onResourceSuccess {
+ it.let { (recipientChips, selectedRecipientChips) ->
+ view?.run {
+ setMailbox(getMailboxName(selectedMailbox))
+ setRecipients(recipientChips)
+ if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(
+ selectedRecipientChips
+ )
+ showContent(true)
}
}
- }.onResourceNotLoading {
- view?.run { showProgress(false) }
- }.launch()
+ }
+ .launch()
}
private fun sendMessage(subject: String, content: String, recipients: List) {
+ val mailbox = selectedMailbox ?: return
+
resourceFlow {
val student = studentRepository.getCurrentStudent()
- val mailbox = mailboxRepository.getMailbox(student)
messageRepository.sendMessage(
student = student,
subject = subject,
@@ -222,18 +248,21 @@ class SendMessagePresenter @Inject constructor(
}
}
- private fun getMailboxName(mailbox: Mailbox): String {
+ private fun getMailboxName(mailbox: Mailbox?): String {
+ mailbox ?: return ""
+
+ // username - accountType [\n student name - ] (school short name)
return buildString {
append(mailbox.userName)
append(" - ")
append(getMailboxType(mailbox.type))
+ appendLine()
if (mailbox.type == MailboxType.PARENT) {
- append(" - ")
append(mailbox.studentName)
+ append(" - ")
}
- append(" - ")
append("(${mailbox.schoolNameShort})")
}
}
@@ -267,9 +296,9 @@ class SendMessagePresenter @Inject constructor(
private fun saveDraftMessage() {
messageRepository.draftMessage = MessageDraft(
- view?.formRecipientsData!!,
- view?.formSubjectValue!!,
- view?.formContentValue!!
+ recipients = view?.formRecipientsData!!,
+ subject = view?.formSubjectValue!!,
+ content = view?.formContentValue!!,
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt
index 1057114b8..e27a09d60 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessageView.kt
@@ -61,4 +61,5 @@ interface SendMessageView : BaseView {
fun getMessageBackupDialogStringWithRecipients(recipients: String): String
fun clearDraft()
+ fun showMailboxChooser(mailboxes: List)
}
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 234d17eb2..6df6153c5 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
@@ -12,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.ItemMessageBinding
import io.github.wulkanowy.databinding.ItemMessageChipsBinding
+import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.toFormattedString
import javax.inject.Inject
@@ -19,13 +20,15 @@ import javax.inject.Inject
class MessageTabAdapter @Inject constructor() :
RecyclerView.Adapter() {
- var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit = { _, _ -> }
+ lateinit var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit
- var onLongItemClickListener: (MessageTabDataItem.MessageItem) -> Unit = {}
+ lateinit var onLongItemClickListener: (MessageTabDataItem.MessageItem) -> Unit
- var onHeaderClickListener: (CompoundButton, Boolean) -> Unit = { _, _ -> }
+ lateinit var onHeaderClickListener: (CompoundButton, Boolean) -> Unit
- var onChangesDetectedListener = {}
+ lateinit var onMailboxClickListener: () -> Unit
+
+ lateinit var onChangesDetectedListener: () -> Unit
private var items = mutableListOf()
@@ -49,12 +52,12 @@ class MessageTabAdapter @Inject constructor() :
val inflater = LayoutInflater.from(parent.context)
return when (MessageItemViewType.values()[viewType]) {
- MessageItemViewType.MESSAGE -> ItemViewHolder(
- ItemMessageBinding.inflate(inflater, parent, false)
- )
MessageItemViewType.FILTERS -> HeaderViewHolder(
ItemMessageChipsBinding.inflate(inflater, parent, false)
)
+ MessageItemViewType.MESSAGE -> ItemViewHolder(
+ ItemMessageBinding.inflate(inflater, parent, false)
+ )
}
}
@@ -69,6 +72,20 @@ class MessageTabAdapter @Inject constructor() :
val item = items[position] as MessageTabDataItem.FilterHeader
with(holder.binding) {
+ chipMailbox.text = item.selectedMailbox
+ ?: root.context.getString(R.string.message_chip_all_mailboxes)
+ chipMailbox.chipBackgroundColor = ColorStateList.valueOf(
+ if (item.selectedMailbox == null) {
+ root.context.getCompatColor(R.color.mtrl_choice_chip_background_color)
+ } else root.context.getThemeAttrColor(android.R.attr.colorPrimary, 64)
+ )
+ chipMailbox.setTextColor(
+ if (item.selectedMailbox == null) {
+ root.context.getThemeAttrColor(android.R.attr.textColorPrimary)
+ } else root.context.getThemeAttrColor(android.R.attr.colorPrimary)
+ )
+ chipMailbox.setOnClickListener { onMailboxClickListener() }
+
if (item.onlyUnread == null) {
chipUnread.isVisible = false
} else {
@@ -77,6 +94,7 @@ class MessageTabAdapter @Inject constructor() :
chipUnread.setOnCheckedChangeListener(onHeaderClickListener)
}
chipUnread.isEnabled = item.isEnabled
+
chipAttachments.isEnabled = item.isEnabled
chipAttachments.isChecked = item.onlyWithAttachments
chipAttachments.setOnCheckedChangeListener(onHeaderClickListener)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt
index 634dfc0e7..c0bd4170e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt
@@ -11,6 +11,7 @@ sealed class MessageTabDataItem(val viewType: MessageItemViewType) {
) : MessageTabDataItem(MessageItemViewType.MESSAGE)
data class FilterHeader(
+ val selectedMailbox: String?,
val onlyUnread: Boolean?,
val onlyWithAttachments: Boolean,
val isEnabled: Boolean
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt
index 654b0e226..5d608ad3b 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
@@ -10,15 +10,18 @@ import android.widget.CompoundButton
import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView
import androidx.core.view.updatePadding
+import androidx.fragment.app.setFragmentResultListener
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.databinding.FragmentMessageTabBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.message.MessageFragment
+import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
import io.github.wulkanowy.utils.dpToPx
@@ -104,6 +107,7 @@ class MessageTabFragment : BaseFragment(R.layout.frag
onItemClickListener = presenter::onMessageItemSelected
onLongItemClickListener = presenter::onMessageItemLongSelected
onHeaderClickListener = ::onChipChecked
+ onMailboxClickListener = presenter::onMailboxFilterSelected
onChangesDetectedListener = ::resetListPosition
}
@@ -123,6 +127,12 @@ class MessageTabFragment : BaseFragment(R.layout.frag
messageTabErrorRetry.setOnClickListener { presenter.onRetry() }
messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
+
+ setFragmentResultListener(requireArguments().getString(MESSAGE_TAB_FOLDER_ID)!!) { _, bundle ->
+ presenter.onMailboxSelected(
+ mailbox = bundle.getSerializable(MailboxChooserDialog.MAILBOX_KEY) as? Mailbox,
+ )
+ }
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -246,6 +256,16 @@ class MessageTabFragment : BaseFragment(R.layout.frag
)
}
+ override fun showMailboxChooser(mailboxes: List) {
+ (activity as? MainActivity)?.showDialogFragment(
+ MailboxChooserDialog.newInstance(
+ mailboxes = mailboxes,
+ isMailboxRequired = false,
+ folder = requireArguments().getString(MESSAGE_TAB_FOLDER_ID)!!,
+ )
+ )
+ }
+
override fun hideKeyboard() {
activity?.hideSoftInput()
}
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 54711a689..ea142db2b 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,9 +1,9 @@
package io.github.wulkanowy.ui.modules.message.tab
import io.github.wulkanowy.data.*
+import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.enums.MessageFolder
-import io.github.wulkanowy.data.repositories.MailboxRepository
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
@@ -26,7 +26,6 @@ class MessageTabPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val messageRepository: MessageRepository,
- private val mailboxRepository: MailboxRepository,
private val analytics: AnalyticsHelper
) : BasePresenter(errorHandler, studentRepository) {
@@ -36,6 +35,9 @@ class MessageTabPresenter @Inject constructor(
private var lastSearchQuery = ""
+ private var mailboxes: List = emptyList()
+ private var selectedMailbox: Mailbox? = null
+
private var messages = emptyList()
private val searchChannel = Channel()
@@ -122,8 +124,7 @@ class MessageTabPresenter @Inject constructor(
runCatching {
val student = studentRepository.getCurrentStudent(true)
- val mailbox = mailboxRepository.getMailbox(student)
- messageRepository.deleteMessages(student, mailbox, messageList)
+ messageRepository.deleteMessages(student, selectedMailbox, messageList)
}
.onFailure(errorHandler::dispatch)
.onSuccess { view?.showMessagesDeleted() }
@@ -202,13 +203,28 @@ class MessageTabPresenter @Inject constructor(
}
}
+ fun onMailboxFilterSelected() {
+ view?.showMailboxChooser(mailboxes)
+ }
+
+ fun onMailboxSelected(mailbox: Mailbox?) {
+ selectedMailbox = mailbox
+ loadData(false)
+ }
+
private fun loadData(forceRefresh: Boolean) {
Timber.i("Loading $folder message data started")
flatResourceFlow {
val student = studentRepository.getCurrentStudent()
- val mailbox = mailboxRepository.getMailbox(student)
- messageRepository.getMessages(student, mailbox, folder, forceRefresh)
+
+ if (selectedMailbox == null && mailboxes.isEmpty()) {
+ selectedMailbox = messageRepository.getMailboxByStudent(student)
+ mailboxes = messageRepository.getMailboxes(student, forceRefresh).toFirstResult()
+ .dataOrNull.orEmpty()
+ }
+
+ messageRepository.getMessages(student, selectedMailbox, folder, forceRefresh)
}
.logResourceStatus("load $folder message")
.onResourceData {
@@ -327,7 +343,16 @@ class MessageTabPresenter @Inject constructor(
MessageTabDataItem.FilterHeader(
onlyUnread = onlyUnread.takeIf { folder != MessageFolder.SENT },
onlyWithAttachments = onlyWithAttachments,
- isEnabled = !isActionMode
+ isEnabled = !isActionMode,
+ selectedMailbox = selectedMailbox?.let {
+ buildString {
+ if (it.studentName.isNotBlank() && it.studentName != it.userName) {
+ append(it.studentName)
+ append(" - ")
+ }
+ append(it.userName)
+ }
+ },
)
)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt
index bfa43b209..6ece6621b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabView.kt
@@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.message.tab
+import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.ui.base.BaseView
@@ -46,4 +47,6 @@ interface MessageTabView : BaseView {
fun showActionMode(show: Boolean)
fun showRecyclerBottomPadding(show: Boolean)
+
+ fun showMailboxChooser(mailboxes: List)
}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt
index c69fec65c..93e67be01 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt
@@ -4,6 +4,7 @@ import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.SharedPrefProvider
+import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder
@@ -25,8 +26,8 @@ fun getRefreshKey(name: String, student: Student): String {
return "${name}_${student.userLoginId}"
}
-fun getRefreshKey(name: String, student: Student, folder: MessageFolder): String {
- return "${name}_${student.id}_${folder.id}"
+fun getRefreshKey(name: String, mailbox: Mailbox?, folder: MessageFolder): String {
+ return "${name}_${mailbox?.globalKey ?: "all"}_${folder.id}"
}
class AutoRefreshHelper @Inject constructor(
diff --git a/app/src/main/res/layout/activity_send_message.xml b/app/src/main/res/layout/activity_send_message.xml
index 320782bdc..a8041d61c 100644
--- a/app/src/main/res/layout/activity_send_message.xml
+++ b/app/src/main/res/layout/activity_send_message.xml
@@ -55,17 +55,29 @@
android:id="@+id/sendMessageFrom"
android:layout_width="0dp"
android:layout_height="58dp"
- android:layout_marginStart="16dp"
- android:layout_marginLeft="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginRight="16dp"
+ android:layout_marginStart="8dp"
+ android:background="?selectableItemBackground"
android:gravity="center_vertical"
+ android:paddingStart="8dp"
+ android:paddingEnd="32dp"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/sendMessageFromHint"
app:layout_constraintTop_toTopOf="parent"
tools:text="Jan Kowalski" />
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_mailbox_chooser.xml b/app/src/main/res/layout/item_mailbox_chooser.xml
new file mode 100644
index 000000000..7c93199bc
--- /dev/null
+++ b/app/src/main/res/layout/item_mailbox_chooser.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_message_chips.xml b/app/src/main/res/layout/item_message_chips.xml
index 481a94835..da2e20311 100644
--- a/app/src/main/res/layout/item_message_chips.xml
+++ b/app/src/main/res/layout/item_message_chips.xml
@@ -1,21 +1,30 @@
-
+ app:layout_constraintTop_toTopOf="parent"
+ app:singleLine="true">
+
+
-
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e65a27fcc..e365a00ca 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -301,6 +301,7 @@
Message does not exist
You need to choose at least 1 recipient
The message content must be at least 3 characters
+ Wszystkie skrzynki
Only unread
Only with attachments
Read: %s
@@ -324,6 +325,7 @@
- %1$d selected
Messages deleted
+ Choose mailbox
diff --git a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
index ff0a53135..84a0cb405 100644
--- a/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
+++ b/app/src/test/java/io/github/wulkanowy/TestEnityCreator.kt
@@ -27,7 +27,9 @@ fun getMailboxEntity() = Mailbox(
globalKey = "v4",
fullName = "",
userName = "",
- userLoginId = 0,
+ email = "test",
+ symbol = "powiatwulkanowy",
+ schoolId = "123456",
studentName = "",
schoolNameShort = "",
type = MailboxType.UNKNOWN,
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
index 9fc83a23b..9a2c22fd6 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
@@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories
import android.content.Context
import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.SharedPrefProvider
+import io.github.wulkanowy.data.db.dao.MailboxDao
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
import io.github.wulkanowy.data.db.dao.MessagesDao
import io.github.wulkanowy.data.db.entities.Message
@@ -10,6 +11,7 @@ import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.toFirstResult
+import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase
import io.github.wulkanowy.getMailboxEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
@@ -55,6 +57,12 @@ class MessageRepositoryTest {
@MockK
private lateinit var sharedPrefProvider: SharedPrefProvider
+ @MockK
+ private lateinit var mailboxDao: MailboxDao
+
+ @MockK
+ private lateinit var getMailboxByStudentUseCase: GetMailboxByStudentUseCase
+
private val student = getStudentEntity()
private val mailbox = getMailboxEntity()
@@ -74,26 +82,33 @@ class MessageRepositoryTest {
refreshHelper = refreshHelper,
sharedPrefProvider = sharedPrefProvider,
json = Json,
+ mailboxDao = mailboxDao,
+ getMailboxByStudentUseCase = getMailboxByStudentUseCase,
)
}
@Test
fun `get messages when fetched completely new message without notify`() = runBlocking {
- every { messageDb.loadAll(any(), any()) } returns flowOf(emptyList())
+ coEvery { mailboxDao.loadAll(any()) } returns listOf(mailbox)
+ every { messageDb.loadAll(mailbox.globalKey, any()) } returns flowOf(emptyList())
coEvery { sdk.getMessages(Folder.RECEIVED, any()) } returns listOf(
- getMessageDto()
+ getMessageDto().copy(
+ unreadBy = 5,
+ readBy = 10,
+ )
)
coEvery { messageDb.deleteAll(any()) } just Runs
coEvery { messageDb.insertAll(any()) } returns listOf()
- repository.getMessages(
+ val res = repository.getMessages(
student = student,
mailbox = mailbox,
folder = MessageFolder.RECEIVED,
forceRefresh = true,
notify = false,
- ).toFirstResult().dataOrNull.orEmpty()
+ ).toFirstResult()
+ assertEquals(null, res.errorOrNull)
coVerify(exactly = 1) { messageDb.deleteAll(withArg { checkEquals(emptyList()) }) }
coVerify {
messageDb.insertAll(withArg {
@@ -187,6 +202,7 @@ class MessageRepositoryTest {
) = Message(
messageGlobalKey = "v4",
mailboxKey = "",
+ email = "",
correspondents = "",
messageId = messageId,
subject = "",
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt
similarity index 77%
rename from app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt
rename to app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt
index 9198560fe..96a84a5a6 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MailboxRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt
@@ -1,65 +1,52 @@
-package io.github.wulkanowy.data.repositories
+package io.github.wulkanowy.domain
import io.github.wulkanowy.data.db.dao.MailboxDao
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.MailboxType
import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.AutoRefreshHelper
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.impl.annotations.MockK
-import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import java.time.Instant
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
@OptIn(ExperimentalCoroutinesApi::class)
-class MailboxRepositoryTest {
-
- @SpyK
- private var sdk = Sdk()
+class GetMailboxByStudentUseCaseTest {
@MockK
private lateinit var mailboxDao: MailboxDao
- @MockK
- private lateinit var refreshHelper: AutoRefreshHelper
-
- private lateinit var systemUnderTest: MailboxRepository
+ private lateinit var systemUnderTest: GetMailboxByStudentUseCase
@Before
fun setUp() {
MockKAnnotations.init(this)
- coEvery { refreshHelper.shouldBeRefreshed(any()) } returns false
- coEvery { refreshHelper.updateLastRefreshTimestamp(any()) } just Runs
coEvery { mailboxDao.deleteAll(any()) } just Runs
coEvery { mailboxDao.insertAll(any()) } returns emptyList()
coEvery { mailboxDao.loadAll(any()) } returns emptyList()
- coEvery { sdk.getMailboxes() } returns emptyList()
- systemUnderTest = MailboxRepository(
- mailboxDao = mailboxDao,
- sdk = sdk,
- refreshHelper = refreshHelper,
- )
+ systemUnderTest = GetMailboxByStudentUseCase(mailboxDao = mailboxDao)
}
- @Test(expected = IllegalArgumentException::class)
+ @Test
fun `get mailbox that doesn't exist`() = runTest {
val student = getStudentEntity(
userName = "Stanisław Kowalski",
studentName = "Jan Kowalski",
)
- coEvery { sdk.getMailboxes() } returns emptyList()
+ coEvery { mailboxDao.loadAll(any()) } returns emptyList()
- systemUnderTest.getMailbox(student)
+ assertNull(systemUnderTest(student))
}
@Test
@@ -73,7 +60,7 @@ class MailboxRepositoryTest {
expectedMailbox,
)
- val selectedMailbox = systemUnderTest.getMailbox(student)
+ val selectedMailbox = systemUnderTest(student)
assertEquals(expectedMailbox, selectedMailbox)
}
@@ -88,7 +75,7 @@ class MailboxRepositoryTest {
expectedMailbox,
)
- assertEquals(expectedMailbox, systemUnderTest.getMailbox(student))
+ assertEquals(expectedMailbox, systemUnderTest(student))
}
@Test
@@ -102,10 +89,10 @@ class MailboxRepositoryTest {
expectedMailbox,
)
- assertEquals(expectedMailbox, systemUnderTest.getMailbox(student))
+ assertEquals(expectedMailbox, systemUnderTest(student))
}
- @Test(expected = IllegalArgumentException::class)
+ @Test
fun `get mailbox for not-unique non-authorized student`() = runTest {
val student = getStudentEntity(
userName = "Stanisław Kowalski",
@@ -116,7 +103,7 @@ class MailboxRepositoryTest {
getMailboxEntity("Jan Kurowski"),
)
- systemUnderTest.getMailbox(student)
+ assertNull(systemUnderTest(student))
}
@Test
@@ -130,7 +117,7 @@ class MailboxRepositoryTest {
expectedMailbox,
)
- assertEquals(expectedMailbox, systemUnderTest.getMailbox(student))
+ assertEquals(expectedMailbox, systemUnderTest(student))
}
@Test
@@ -144,7 +131,7 @@ class MailboxRepositoryTest {
expectedMailbox,
)
- assertEquals(expectedMailbox, systemUnderTest.getMailbox(student))
+ assertEquals(expectedMailbox, systemUnderTest(student))
}
@Test
@@ -158,7 +145,7 @@ class MailboxRepositoryTest {
expectedMailbox,
)
- assertEquals(expectedMailbox, systemUnderTest.getMailbox(student))
+ assertEquals(expectedMailbox, systemUnderTest(student))
}
private fun getMailboxEntity(
@@ -167,7 +154,9 @@ class MailboxRepositoryTest {
globalKey = "",
fullName = "",
userName = "",
- userLoginId = 123,
+ email = "",
+ schoolId = "",
+ symbol = "",
studentName = studentName,
schoolNameShort = "",
type = MailboxType.STUDENT,
From d3e276d6fc79d4806befce5c9f43826b4f4ec4bb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Wed, 16 Nov 2022 20:13:48 +0100
Subject: [PATCH 272/669] New Crowdin updates (#2049)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Mikołaj Pich
---
app/src/main/res/values-cs/strings.xml | 5 +-
app/src/main/res/values-de/strings.xml | 3 +
app/src/main/res/values-es-rES/strings.xml | 3 +
app/src/main/res/values-pl/strings.xml | 3 +
app/src/main/res/values-ru/strings.xml | 41 +++++-----
app/src/main/res/values-sk/strings.xml | 5 +-
app/src/main/res/values-uk/strings.xml | 25 +++---
app/src/main/res/values/strings.xml | 92 +---------------------
8 files changed, 55 insertions(+), 122 deletions(-)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index d036e7e42..f41cb17f9 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -309,9 +309,11 @@
Zpráva neexistuje
Musíte vybrat alespoň 1 příjemce
Obsah zprávy musí mít alespoň 3 znaky
+ Všechny poštovní schránky
Pouze nepřečtené
Pouze s přílohami
Přečtena: %s
+ Přečtena přes: %1$d z %2$d osob
- %1$d zpráva
- %1$d zprávy
@@ -339,6 +341,7 @@
- %1$d vybraných
Zprávy odstraněné
+ Vyberte poštovní schránku
Žádné informace o poznámkách
Body
@@ -735,7 +738,7 @@
Volbu můžete kdykoliv změnit v nastavení aplikace. Můžeme použít Vaše data k zobrazení reklam šitých pro vás nebo pomocí méně vašich dat zobrazovat nepřizpůsobené reklamy. Podrobnosti naleznete v našich Zásadách ochrany osobních údajů
Přizpůsobené reklamy
Nepřizpůsobené reklamy
- Mám ukončené 18 let
+ Je mi více než 18 let
Ano, přizpůsobené reklamy
Ano, nepřizpůsobené reklamy
Pokročilé
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 7b544258a..6107fbb96 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -275,9 +275,11 @@
Nachricht nicht vorhanden
Sie müssen mindestens 1 Empfänger auswählen.
Der Inhalt der Nachricht muss mindestens 3 Zeichen lang sein.
+ All mailboxes
Nur ungelesen
Nur mit Anhängen
Lesen: %s
+ Read by: %1$d of %2$d people
- %1$d Nachricht
- %1$d Nachrichten
@@ -297,6 +299,7 @@
- %1$d ausgewählt
Nachrichten gelöscht
+ Choose mailbox
Keine Informationen über Eintragen
Punkte
diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml
index aecc720b7..95a00a602 100644
--- a/app/src/main/res/values-es-rES/strings.xml
+++ b/app/src/main/res/values-es-rES/strings.xml
@@ -275,9 +275,11 @@
Message does not exist
You need to choose at least 1 recipient
The message content must be at least 3 characters
+ All mailboxes
Only unread
Only with attachments
Read: %s
+ Read by: %1$d of %2$d people
- %1$d message
- %1$d messages
@@ -297,6 +299,7 @@
- %1$d selected
Messages deleted
+ Choose mailbox
No info about notes
Points
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index c9abbd022..f612d826d 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -309,9 +309,11 @@
Wiadomość nie istnieje
Musisz wybrać co najmniej 1 adresata
Treść wiadomości musi zawierać co najmniej 3 znaki
+ Wszystkie skrzynki
Tylko nieprzeczytane
Tylko z załącznikami
Przeczytana: %s
+ Przeczytana przez: %1$d z %2$d osób
- %1$d wiadomość
- %1$d wiadomości
@@ -339,6 +341,7 @@
- %1$d wybranych
Wiadomości zostały usunięte
+ Wybierz skrzynkę
Brak informacji o uwagach
Punkty
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index e1abb0293..195371fd0 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -77,9 +77,9 @@
Войти
Сеанс истёк
Сеанс истёк, авторизуйтесь снова
- Application support
- Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time
- Enable ads
+ Поддержка приложения
+ Вам нравится это приложение? Поддержите его разработку, включив неинвазивную рекламу, которую можно отключить в любое время
+ Включить рекламу
Оценка
%d семестр
@@ -97,7 +97,7 @@
Ожидаемая оценка
Рассчитанная средняя оценка
Как работает \"Рассчитанная средняя оценка\"?
- The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\nAverage of grades only from selected semester:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\nAverage of averages from both semesters:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\nAverage of grades from the whole year:\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages
+ Рассчитанная средняя оценка - это среднее арифметическое, рассчитанное на основе средних оценок по предметам. Это позволяет узнать приблизительную итоговую среднюю оценку. Она рассчитывается способом, выбранным пользователем в настройках приложения. Рекомендуется выбрать подходящий вариант, так как каждая школа по разному считает среднюю оценку. Кроме того, если ваша школа выставляет средние оценки по предметам на странице Vulcan, приложение просто загрузит их. Это можно изменить, заставив приложение считать среднюю оценку в настройках.\n\nСредняя из оценок выбранного семестра:\n1. Вычисление средневзвешенного значения по каждому предмету за семестр\n2.Суммирование вычисленных значений\n3. Вычисление среднего арифметического суммированных значений\n\nСредняя из средних оценок семестров:\n1.Расчет средневзвешенного значения для каждого предмета в семестрах. \n2. Вычисление среднего арифметического из средневзвешенных значений для каждого предмета в семестрах.\n3. Суммирование средних арифметических\n4. Вычисление среднего арифматического из суммированных значений\n\nСредняя из оценок со всего года:\n1. Расчет средневзвешенного значения по каждому предмету за год. Итоговое среднее значение за 1 семестр не имеет значения.\n2. Суммирование вычисленных средних\n3. Расчет среднего арифметического суммированных чисел
Как работает \"Итоговая средняя оценка\"?
Итоговая средняя оценка - это среднее арифметическое, рассчитанное из всех имеющихся на данный момент итоговых оценок в семестре.\n\nРассчет происходит следующим образом:\n1. Суммирование итоговых оценок, выставленных преподавателями\n2. Полученная сумма делится на число предметов, по которым выставлены оценки
Итоговая средняя оценка
@@ -297,10 +297,10 @@
Перенести в корзину
Удалить навсегда
Письмо успешно удалено
- student
- parent
- guardian
- employee
+ ученик
+ родитель
+ опекун
+ работник
Поделиться
Печать
Тема
@@ -309,9 +309,11 @@
Письма не существует
Вы должны выбрать как минимум одного получателя
Текст сообщения должен содержать как минимум 3 знака
+ Все почтовые ящики
Только непрочитанные
Только с вложениями
Прочитано: %s
+ Прочитано: %1$d из %2$d человек
- %1$d сообщение
- %1$d сообщения
@@ -339,6 +341,7 @@
- %1$d выбрано
Сообщение удалено
+ Выбрать почтовый ящик
Нет записей о замечаниях и свершениях
Баллы
@@ -720,10 +723,10 @@
Отвечать с историей сообщений
Показывать среднее арифметическое при отсутствии стоимости
Поддержка
- Privacy Policy
- Agreements
- Consent to processing of data related to ads
- Show ads in app
+ Политика приватности
+ Соглашения
+ Согласие на обработку данных, связанных с объявлениями
+ Показать рекламу в приложении
Посмотреть рекламу для поддержки проекта
Согласие на обработку данных
Для просмотра рекламы вы должны согласиться с условиями обработки данных нашей Политики конфиденциальности
@@ -731,13 +734,13 @@
Политика конфиденциальности
Реклама загружается
Спасибо за вашу поддержку, возвращайтесь позже для дополнительной рекламы
- Can we use your data to display ads?
- You can change your choice anytime in the app settings. We may use your data to display ads tailored to you or, using less of your data, display non-personalized ads. Please see our Privacy Policy for details
- Personalized ads
- Non-personalized ads
- I am over 18 years old
- Yes, personalized ads
- Yes, non-personalized ads
+ Можем ли мы использовать ваши данные для показа рекламы?
+ Вы можете изменить свой выбор в любое время в настройках приложения. Мы можем использовать ваши данные для показа объявлений в соответствии с вашими пожеланиями или, используя меньше данных, отображать неперсональную рекламу. Пожалуйста, ознакомьтесь с нашей политикой конфиденциальности для подробностей
+ Персонализированная реклама
+ Неперсонализированная реклама
+ Я старше 18 лет
+ Да, персонализировать рекламу
+ Да, не персонализировать рекламу
Расширенные
Внешний вид и поведение
Уведомления
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 1c6eae8c0..9fd06bcbe 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -309,9 +309,11 @@
Správa neexistuje
Musíte vybrať aspoň 1 príjemca
Obsah správy musí mať aspoň 3 znaky
+ Všetky poštové schránky
Iba neprečítané
Iba s prílohami
Prečítaná: %s
+ Prečítaná cez: %1$d z %2$d osôb
- %1$d správa
- %1$d správy
@@ -339,6 +341,7 @@
- %1$d vybraných
Správy odstránené
+ Vyberte poštovú schránku
Žiadne informácie o poznámkach
Body
@@ -735,7 +738,7 @@
Voľbu môžete kedykoľvek zmeniť v nastavení aplikácie. Môžeme použiť vaše údaje na zobrazenie reklám šitých pre vás alebo pomocou menej vašich dát zobrazovať neprispôsobené reklamy. Podrobnosti nájdete v našich Zásadách ochrany osobných údajov
Prispôsobené reklamy
Neprispôsobené reklamy
- Mám ukončené 18 rokov
+ Mám viac ako 18 rokov
Áno, prispôsobené reklamy
Áno, neprispôsobené reklamy
Pokročilé
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 2c104ecbf..2d3bf3724 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -77,8 +77,8 @@
Увійти
Минув термін дії сесії
Минув термін дії сесії, авторизуйтеся знову
- Підтримка додатків
- Вам подобається цей додаток? Підтримуйте його розвиток, увімкнувши неінвазивну рекламу, яку ви можете будь-коли вимкнути
+ Підтримка додатку
+ Вам подобається цей додаток? Підтримайте його розвиток, увімкнувши неінвазивну рекламу, яку ви можете відключити в будь-який час
Увімкнути рекламу
Оцінка
@@ -97,7 +97,7 @@
Передбачувана оцінка
Розрахована середня оцінка
Як працює \"Розрахована середня оцінка\"?
- Розрахована середня оцінка - це середнє арифметичне, обчислене з середніх оцінок з предметів.Це дозволяє дізнатися приблизну кінцеву середню оцінку.Вона розраховується способом, обраним користувачем у налаштуваннях програми.Рекомендується вибрати відповідний варіант, тому що кожна школа по різному розраховує середню оцінку.Крім того, якщо у вашій школі повідомляється середня оцінка з предметів на сторінці Vulcan, програма тільки завантажує ці оцінки.Це можна змінити шляхом примусового розрахунку середньоЇ оцінки в налаштуваннях програми.\n\nСередні оцінки тільки за обраний семестр:\n1. Розрахунок середньозваженого числа для кожного предмета в даному семестрі\n2. Сумування розрахованих числ\n3. Розрахунок середнього арифметичного з сумованих чисел\n\nСереднє значення з обох семестрів:\n1. Обчислення середньозваженого числа для кожного предмета у 1 та 2 семестрі\n2. Обчислення середнього арифметичного з розрахованих середньозважених числ за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахованих середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого числа за рік для кожного предмета. Підсумковий середній показник у 1-му семестрі не має значення.\n2. Сумування розрахованих середніх\n3. Обчислення середнього арифметичного з суммованих середніх
+ Розрахована середня оцінка - це середнє арифметичне, обчислене з середніх оцінок з предметів. Це дозволяє дізнатися приблизну кінцеву середню оцінку. Вона розраховується спосібом, обраним користувачем у налаштуваннях програми. Рекомендується вибрати відповідний варіант, тому що кожна школа по різному розраховує середню оцінку. Крім того, якщо у вашій школі повідомляється середня оцінка з предметів на сторінці Vulcan, програма тільки завантажує ці оцінки і не розраховує їх самостійно. Це можна змінити шляхом примусового розрахунку середньоЇ оцінки в налаштуваннях програми.\n\nСередні оцінки тільки за обраний семестр:\n1. Розрахунок середньозваженого числа для кожного предмета в даному семестрі\n2. Сумування розрахованих числ\n3. Розрахунок середнього арифметичного з сумованих чисел\n\nСереднє значення з обох семестрів:\n1. Обчислення середньозваженого числа для кожного предмета у 1 та 2 семестрі\n2. Обчислення середнього арифметичного з розрахованих середньозважених числ за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахованих середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\nСереднє значення оцінок за весь рік: \n1. Розрахунок середньозваженого числа за рік для кожного предмета. Підсумковий середній показник у 1-му семестрі не має значення.\n2. Сумування розрахованих середніх\n3. Обчислення середнього арифметичного з суммованих середніх
Як працює \"Підсумкова середня оцінка\"?
Підсумкове середнє значення - це середнє арифметичне, обчислене з усіх наявних наразі підсумкових оцінок у даному семестрі. \n\nСхема обчислення складається з таких кроків:\n1. Сумування підсумкових оцінок, виставленних викладачами\n2. Ділення на кількість предметів, з яких виставлені ці оцінки
Підсумкова середня оцінка
@@ -220,7 +220,7 @@
Всі в серії
Час початку
Час завершення
- Час завершення має бути більшим, ніж час початку
+ Час завершення має бути пізніше часу початку
Підсумок відвідуваності
Відсутність зі шкільних причин
@@ -297,8 +297,8 @@
Перемістити до кошика
Видалити назавжди
Лист було успішно видалено
- студент
- батькові
+ учень
+ родич
опікун
працівник
Поділитись
@@ -309,9 +309,11 @@
Такого листа не існує
Необхідно обрати принаймні 1 адресата
Зміст листа повинен складатися принаймні з 3 знаків
+ Усі поштові скриньки
Лише непрочитані
Тільки з вкладеннями
Прочитаний: %s
+ Прочитано: %1$d з %2$d осіб
- %1$d лист
- %1$d листи
@@ -338,7 +340,8 @@
- %1$d вибрано
- %1$d вибрано
- Листи видалені
+ Листи видалено
+ Вибрати поштову скриньку
Немає інформації о зауваженнях
Бали
@@ -722,7 +725,7 @@
Підтримка
Політика конфіденційності
Угоди
- Згода на обробку даних, пов’язаних з оголошеннями
+ Згода на обробку даних, пов\'язаних з рекламою
Показувати рекламу в додатку
Подивіться одну рекламу для підтримки проєкту
Згода в обробці даних
@@ -731,9 +734,9 @@
Політика конфіденційності
Реклама завантажується
Дякуємо за вашу підтримку, повертайтеся пізніше для більшої кількості реклам
- Чи можемо ми використовувати ваші дані для відбивання реклами?
- Ви можете змінити свій вибір в будь-який час в налаштуваннях додатку. Ми можемо використовувати ваші дані для відбивання оголошень, адаптованої до вас або, використовуючи менше ваших даних, відбивати неперсоналізовані оголошення. Перегляньте нашу Політику конфіденційності для деталей
- Персоналізовані оголошення
+ Чи можемо ми використовувати ваші дані для висвітлювання реклами?
+ Ви можете змінити свій вибір в будь-який час в налаштуваннях додатку. Ми можемо використовувати ваші дані для висвітлювання реклами, адаптованої до вас або, використовуючи менше ваших даних, висвітлювати неперсоналізовану рекламу. Перегляньте нашу Політику конфіденційності для подробиць
+ Персоналізована реклама
Неперсоналізована реклама
Мені більше 18 років
Так, персоналізована реклама
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e365a00ca..71d1767ed 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -26,15 +26,11 @@
Student info
Dashboard
Notifications center
-
-
Semester %1$d, %2$d/%3$d
-
-
Sign in with the student or parent account
- Enter the symbol from the register page for account: <b>%1$s</b>
+ Enter the symbol from the register page for account: <b>%1$s</b>
Username
Email
Login, PESEL or e-mail
@@ -78,8 +74,6 @@
Recover
Student is already signed in
Standard
-
-
Account manager
Log in
@@ -88,8 +82,6 @@
Application support
Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time
Enable ads
-
-
Grade
Semester %d
@@ -152,8 +144,6 @@
- You received %1$d final grade
- You received %1$d final grades
-
-
Lesson
Room
@@ -189,8 +179,6 @@
- %d change
- %d changes
-
-
Completed lessons
Show completed lessons
@@ -198,8 +186,6 @@
Topic
Absence
Resources
-
-
Additional lessons
Show additional lessons
@@ -215,8 +201,6 @@
Start time
End time
End time must be greater than start time
-
-
Attendance summary
Absent for school reasons
@@ -249,12 +233,8 @@
- %d attendance
- %d attendance
-
-
Total
-
-
No exams this week
Type
@@ -271,8 +251,6 @@
- %d exam
- %d exams
-
-
Inbox
Sent
@@ -301,7 +279,7 @@
Message does not exist
You need to choose at least 1 recipient
The message content must be at least 3 characters
- Wszystkie skrzynki
+ All mailboxes
Only unread
Only with attachments
Read: %s
@@ -326,8 +304,6 @@
Messages deleted
Choose mailbox
-
-
No info about notes
Points
@@ -343,7 +319,6 @@
- You received %1$d note
- You received %1$d notes
-
- %d praise
@@ -357,7 +332,6 @@
- You received %1$d praise
- You received %1$d praises
-
- %d neutral note
@@ -371,8 +345,6 @@
- You received %1$d neutral note
- You received %1$d neutral notes
-
-
No info about homework
Mark as done
@@ -393,8 +365,6 @@
- %d homework
- %d homework
-
-
Lucky number
Today\'s lucky number is
@@ -402,13 +372,9 @@
Lucky number for today
Today\'s lucky number is: %s
Show history
-
-
Lucky number history
No info about lucky numbers
-
-
Mobile devices
No devices
@@ -418,12 +384,8 @@
Token
Symbol
PIN
-
-
School and teachers
-
-
School
No info about school
@@ -434,14 +396,10 @@
Name of pedagogue
Show on map
Call
-
-
Teachers
No info about teachers
No subject
-
-
Conferences
No info about conferences
@@ -459,7 +417,6 @@
Present at conference
Agenda
-
School announcements
No school announcements
@@ -475,8 +432,6 @@
- You have %1$d new school announcement
- You have %1$d new school announcements
-
-
Add account
Logout
@@ -491,8 +446,6 @@
Contact
Residence details
Personal information
-
-
App version
Contributors
@@ -516,17 +469,11 @@
Licenses
Licenses of libraries used in the application
Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nIdentyfikator instalacji: %4$s\nTreść zgłoszenia:
-
-
License
-
-
Avatar
See more on GitHub
-
-
No info about student or student family
Name
@@ -549,19 +496,13 @@
Female
Last name
Guardian
-
-
Nick
Add nick
Choose avatar color
-
-
Share logs
Refresh
-
-
Lessons
(Tomorrow)
@@ -580,7 +521,6 @@
until %1$s
No upcoming lessons
An error occurred while loading the lessons
-
Homework
No homework to do
An error occurred while loading the homework
@@ -589,11 +529,9 @@
- %1$d more homework
due %1$s
-
Last grades
No new grades
An error occurred while loading the grades
-
School announcements
No current announcements
An error occurred while loading the announcements
@@ -601,7 +539,6 @@
- %1$d more announcement
- %1$d more announcements
-
Exams
No upcoming exams
An error occurred while loading the exams
@@ -609,7 +546,6 @@
- %1$d more exam
- %1$d more exams
-
Conferences
No upcoming conferences
An error occurred while loading the conferences
@@ -617,16 +553,11 @@
- %1$d more conference
- %1$d more conferences
-
An error occurred while loading data
None
-
-
Check for updates
Before reporting a bug, check first if an update with the bug fix is available
-
-
Content
Retry
@@ -654,16 +585,12 @@
Undo
Change
Add to calendar
-
-
No lessons
Choose theme
Light
Dark
System Theme
-
-
App
Default view
@@ -679,7 +606,6 @@
Grades color scheme
Subjects sorting
Language
-
Notifications
Other
Show notifications
@@ -699,7 +625,6 @@
Upcoming lesson notifications
You must allow the Wulkanowy app to set alarms and reminders in your system settings to use this feature.
Go to settings
-
Synchronization
Automatic update
Suspended on holidays
@@ -710,12 +635,10 @@
Sync failed
Sync in progress
Last full sync: %s
-
Value of the plus
Value of the minus
Reply with message history
Show arithmetic average when no weights provided
-
Support
Privacy Policy
Agreements
@@ -735,13 +658,11 @@
I am over 18 years old
Yes, personalized ads
Yes, non-personalized ads
-
Advanced
Appearance & Behavior
Notifications
Synchronization
Advertisements
-
Grades
Dashboard
Tiles visibility
@@ -750,7 +671,6 @@
Grades
Calculated average
Messages
-
Appearance & Behavior
Languages, themes, subjects sorting
App notifications, fix problems
@@ -761,8 +681,6 @@
Advanced
App version, contributors, social portals
Displaying advertisements, project support
-
-
New grades
New homework
@@ -777,8 +695,6 @@
Debug
Timetable change
New attendance
-
-
Black
Red
@@ -786,15 +702,11 @@
Green
Purple
No color
-
-
Download of updates has started…
An update has just been downloaded.
Restart
Update failed! Wulkanowy may not function properly. Consider updating
-
-
No internet connection
An error occurred. Check your device clock
From 2d83218f618fcb927e9a6247817623ed5562e301 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Wed, 16 Nov 2022 20:31:02 +0100
Subject: [PATCH 273/669] Version 1.8.0
---
app/build.gradle | 10 +++++-----
app/src/main/play/release-notes/pl-PL/default.txt | 10 ++++++----
2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 99672f1f9..62a5a21fb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,8 +23,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 32
- versionCode 114
- versionName "1.7.5"
+ versionCode 115
+ versionName "1.8.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -160,8 +160,8 @@ kapt {
play {
defaultToAppBundles = false
track = 'production'
-// releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
-// userFraction = 0.05d
+ releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
+ userFraction = 0.25d
updatePriority = 4
enabled.set(false)
}
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:701016eda2"
+ implementation "io.github.wulkanowy:sdk:1.8.0"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
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 064401abd..996d5eebc 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,9 @@
-Wersja 1.7.5
+Wersja 1.8.0
-- naprawiliśmy kilka błędów w obsłudze nowego modułu wiadomości
-- naprawiliśmy wyświetlanie napisu "Brak ocen", jesli uczeń nie zdobył w danym semestrze jeszcze żadnych ocen
-- naprawiliśmy logowanie do aplikacji rodzicom będącym jednocześnie nauczycielami
+- naprawiliśmy liczenie średniej ucznia w ocenach klasy dla wykresu "Wszystkie"
+- zmieniliśmy kolejność przycisków akcji w podglądzie wiadomości
+- ulepszyliśmy oznaczenie nieodczytanych wiadomości
+- naprawiliśmy pokazywanie informacji o odczytanej wiadomości w wysłanych
+- dodaliśmy opcję ręcznego wybierania skrzynki pocztowej
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
From 6c115fb915cfcc29c1a25e3f94476c473a4e1ed2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 18 Nov 2022 16:32:04 +0100
Subject: [PATCH 274/669] Fallback to subject name from timetable when
attendance item doesn't have it (#2052)
* Fallback to subject name from timetable when attendance item doesn't have it
* Fix tests
* Add some unit tests
---
.../java/io/github/wulkanowy/data/Resource.kt | 2 +-
.../wulkanowy/data/db/dao/TimetableDao.kt | 3 +
.../data/mappers/AttendanceMapper.kt | 9 +-
.../data/repositories/AttendanceRepository.kt | 11 +-
.../data/mappers/AttendanceMapperKtTest.kt | 143 ++++++++++++++++++
.../repositories/AttendanceRepositoryTest.kt | 27 ++--
6 files changed, 180 insertions(+), 15 deletions(-)
create mode 100644 app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt
diff --git a/app/src/main/java/io/github/wulkanowy/data/Resource.kt b/app/src/main/java/io/github/wulkanowy/data/Resource.kt
index 44f8a1b48..6b611e477 100644
--- a/app/src/main/java/io/github/wulkanowy/data/Resource.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/Resource.kt
@@ -49,8 +49,8 @@ fun Resource.mapData(block: (T) -> U) = when (this) {
fun Flow>.logResourceStatus(name: String, showData: Boolean = false) = onEach {
val description = when (it) {
- is Resource.Loading -> "started"
is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
+ is Resource.Loading -> "started"
is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else ""
is Resource.Error -> "exception occurred: ${it.error}"
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt
index 5e6eec668..b4b7379f2 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt
@@ -13,4 +13,7 @@ interface TimetableDao : BaseDao {
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow>
+
+ @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
+ fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt
index 46e67fdaa..c0ed0c8c2 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/AttendanceMapper.kt
@@ -3,17 +3,22 @@ package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance
import io.github.wulkanowy.sdk.pojo.AttendanceSummary as SdkAttendanceSummary
-fun List.mapToEntities(semester: Semester) = map {
+fun List.mapToEntities(semester: Semester, lessons: List) = map {
Attendance(
studentId = semester.studentId,
diaryId = semester.diaryId,
date = it.date,
timeId = it.timeId,
number = it.number,
- subject = it.subject,
+ subject = it.subject.ifBlank {
+ lessons.find { lesson ->
+ lesson.date == it.date && lesson.number == it.number
+ }?.subject.orEmpty()
+ },
name = it.name,
presence = it.presence,
absence = it.absence,
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
index 9aa6562a6..fd5d8bd16 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
@@ -1,6 +1,7 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.AttendanceDao
+import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
@@ -9,8 +10,10 @@ import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Absent
import io.github.wulkanowy.utils.*
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.withContext
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
@@ -20,6 +23,7 @@ import javax.inject.Singleton
@Singleton
class AttendanceRepository @Inject constructor(
private val attendanceDb: AttendanceDao,
+ private val timetableDb: TimetableDao,
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
@@ -48,10 +52,15 @@ class AttendanceRepository @Inject constructor(
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
},
fetch = {
+ val lessons = withContext(Dispatchers.IO) {
+ timetableDb.load(
+ semester.diaryId, semester.studentId, start.monday, end.sunday
+ )
+ }
sdk.init(student)
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
.getAttendance(start.monday, end.sunday, semester.semesterId)
- .mapToEntities(semester)
+ .mapToEntities(semester, lessons)
},
saveFetchResult = { old, new ->
attendanceDb.deleteAll(old uniqueSubtract new)
diff --git a/app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt b/app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt
new file mode 100644
index 000000000..a35e5d303
--- /dev/null
+++ b/app/src/test/java/io/github/wulkanowy/data/mappers/AttendanceMapperKtTest.kt
@@ -0,0 +1,143 @@
+package io.github.wulkanowy.data.mappers
+
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Timetable
+import io.github.wulkanowy.sdk.pojo.Attendance
+import io.github.wulkanowy.sdk.scrapper.attendance.SentExcuse
+import org.junit.Test
+import java.time.Instant
+import java.time.LocalDate
+import kotlin.test.assertEquals
+
+class AttendanceMapperTest {
+
+ @Test
+ fun `map attendance when fallback is not necessary`() {
+ val attendance = listOf(
+ getSdkAttendance(1, LocalDate.of(2022, 11, 17), "Oryginalna 1"),
+ getSdkAttendance(2, LocalDate.of(2022, 11, 17), "Oryginalna 2"),
+ )
+ val lessons = listOf(
+ getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"),
+ getEntityTimetable(2, LocalDate.of(2022, 11, 17), "Druga"),
+ )
+
+ val result = attendance.mapToEntities(getEntitySemester(), lessons)
+ assertEquals("Oryginalna 1", result[0].subject)
+ assertEquals("Oryginalna 2", result[1].subject)
+ }
+
+ @Test
+ fun `map attendance when fallback is not always necessary`() {
+ val attendance = listOf(
+ getSdkAttendance(1, LocalDate.of(2022, 11, 17), "Oryginalna 1"),
+ getSdkAttendance(2, LocalDate.of(2022, 11, 17), ""),
+ )
+ val lessons = listOf(
+ getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"),
+ getEntityTimetable(2, LocalDate.of(2022, 11, 17), "Druga"),
+ )
+
+ val result = attendance.mapToEntities(getEntitySemester(), lessons)
+ assertEquals("Oryginalna 1", result[0].subject)
+ assertEquals("Druga", result[1].subject)
+ }
+
+ @Test
+ fun `map attendance when fallback is sometimes empty`() {
+ val attendance = listOf(
+ getSdkAttendance(1, LocalDate.of(2022, 11, 17), "Oryginalna 1"),
+ getSdkAttendance(2, LocalDate.of(2022, 11, 17), ""),
+ )
+ val lessons = listOf(
+ getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"),
+ )
+
+ val result = attendance.mapToEntities(getEntitySemester(), lessons)
+ assertEquals("Oryginalna 1", result[0].subject)
+ assertEquals("", result[1].subject)
+ }
+
+ @Test
+ fun `map attendance when fallback is empty`() {
+ val attendance = listOf(
+ getSdkAttendance(1, LocalDate.of(2022, 11, 17), ""),
+ getSdkAttendance(2, LocalDate.of(2022, 11, 17), ""),
+ )
+ val lessons = listOf(
+ getEntityTimetable(1, LocalDate.of(2022, 11, 18), "Pierwsza"),
+ getEntityTimetable(2, LocalDate.of(2022, 10, 17), "Druga"),
+ )
+
+ val result = attendance.mapToEntities(getEntitySemester(), lessons)
+ assertEquals("", result[0].subject)
+ assertEquals("", result[1].subject)
+ }
+
+ @Test
+ fun `map attendance with all subject fallback`() {
+ val attendance = listOf(
+ getSdkAttendance(1, LocalDate.of(2022, 11, 17)),
+ getSdkAttendance(2, LocalDate.of(2022, 11, 17)),
+ )
+ val lessons = listOf(
+ getEntityTimetable(1, LocalDate.of(2022, 11, 17), "Pierwsza"),
+ getEntityTimetable(2, LocalDate.of(2022, 11, 17), "Druga"),
+ )
+
+ val result = attendance.mapToEntities(getEntitySemester(), lessons)
+ assertEquals("Pierwsza", result[0].subject)
+ assertEquals("Druga", result[1].subject)
+ }
+
+ private fun getSdkAttendance(number: Int, date: LocalDate, subject: String = "") = Attendance(
+ number = number,
+ name = "ABSENCE",
+ subject = subject,
+ date = date,
+ timeId = 1,
+ categoryId = 1,
+ deleted = false,
+ excuseStatus = SentExcuse.Status.WAITING,
+ excusable = false,
+ absence = false,
+ excused = false,
+ exemption = false,
+ lateness = false,
+ presence = false,
+ )
+
+ private fun getEntityTimetable(number: Int, date: LocalDate, subject: String = "") = Timetable(
+ number = number,
+ start = Instant.now(),
+ end = Instant.now(),
+ date = date,
+ subject = subject,
+ subjectOld = "",
+ group = "",
+ room = "",
+ roomOld = "",
+ teacher = "",
+ teacherOld = "",
+ info = "",
+ changes = false,
+ canceled = false,
+ studentId = 0,
+ diaryId = 0,
+ isStudentPlan = false,
+ )
+
+ private fun getEntitySemester() = Semester(
+ studentId = 0,
+ diaryId = 0,
+ kindergartenDiaryId = 0,
+ diaryName = "",
+ schoolYear = 0,
+ semesterId = 0,
+ semesterName = 0,
+ start = LocalDate.now(),
+ end = LocalDate.now(),
+ classId = 0,
+ unitId = 0
+ )
+}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
index 7d22f7265..896491ef0 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
@@ -2,6 +2,7 @@ package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.dao.AttendanceDao
+import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.toFirstResult
@@ -29,6 +30,9 @@ class AttendanceRepositoryTest {
@MockK
private lateinit var attendanceDb: AttendanceDao
+ @MockK
+ private lateinit var timetableDb: TimetableDao
+
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
@@ -51,8 +55,9 @@ class AttendanceRepositoryTest {
fun setUp() {
MockKAnnotations.init(this)
every { refreshHelper.shouldBeRefreshed(any()) } returns false
+ coEvery { timetableDb.load(any(), any(), any(), any()) } returns emptyList()
- attendanceRepository = AttendanceRepository(attendanceDb, sdk, refreshHelper)
+ attendanceRepository = AttendanceRepository(attendanceDb, timetableDb, sdk, refreshHelper)
}
@Test
@@ -60,8 +65,8 @@ class AttendanceRepositoryTest {
// prepare
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
- flowOf(remoteList.mapToEntities(semester)),
- flowOf(remoteList.mapToEntities(semester))
+ flowOf(remoteList.mapToEntities(semester, emptyList())),
+ flowOf(remoteList.mapToEntities(semester, emptyList()))
)
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { attendanceDb.deleteAll(any()) } just Runs
@@ -83,9 +88,9 @@ class AttendanceRepositoryTest {
// prepare
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
- flowOf(remoteList.dropLast(1).mapToEntities(semester)),
- flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
- flowOf(remoteList.mapToEntities(semester))
+ flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())),
+ flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())), // after fetch end before save result
+ flowOf(remoteList.mapToEntities(semester, emptyList()))
)
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { attendanceDb.deleteAll(any()) } just Runs
@@ -100,7 +105,7 @@ class AttendanceRepositoryTest {
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify {
attendanceDb.insertAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
+ it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
})
}
coVerify { attendanceDb.deleteAll(match { it.isEmpty() }) }
@@ -111,9 +116,9 @@ class AttendanceRepositoryTest {
// prepare
coEvery { sdk.getAttendance(startDate, endDate, 1) } returns remoteList.dropLast(1)
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
- flowOf(remoteList.mapToEntities(semester)),
- flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
- flowOf(remoteList.dropLast(1).mapToEntities(semester))
+ flowOf(remoteList.mapToEntities(semester, emptyList())),
+ flowOf(remoteList.mapToEntities(semester, emptyList())), // after fetch end before save result
+ flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList()))
)
coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
coEvery { attendanceDb.deleteAll(any()) } just Runs
@@ -129,7 +134,7 @@ class AttendanceRepositoryTest {
coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
coVerify {
attendanceDb.deleteAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
+ it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
})
}
}
From b16036774460ab500303ab7cb8093ee1584aa8ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 19 Nov 2022 08:52:35 +0100
Subject: [PATCH 275/669] Add support for reversed student name mailbox
matching (#2051)
* Add support for reversed student name mailbox matching
* Add additional log stmt to mailbox matching in all mailbox load
* Revert changes in mapper
---
.../github/wulkanowy/data/mappers/MessageMapper.kt | 2 +-
.../domain/messages/GetMailboxByStudentUseCase.kt | 10 ++++++++++
.../domain/GetMailboxByStudentUseCaseTest.kt | 12 ++++++++++++
3 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
index 8825c5744..87111dd4d 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
@@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
-fun List.mapToEntities(student: Student, mailbox: Mailbox?, allMailboxes: List) = map {
+fun List.mapToEntities(student: Student, mailbox: Mailbox?, allMailboxes: List): List = map {
Message(
messageGlobalKey = it.globalKey,
mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box ->
diff --git a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt
index d96794e51..a696d9b2f 100644
--- a/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt
+++ b/app/src/main/java/io/github/wulkanowy/domain/messages/GetMailboxByStudentUseCase.kt
@@ -21,6 +21,8 @@ class GetMailboxByStudentUseCase @Inject constructor(
it.studentName.normalizeStudentName() == normalizedStudentName
} ?: singleOrNull {
it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart()
+ } ?: singleOrNull {
+ it.studentName.getReversedName() == normalizedStudentName
} ?: singleOrNull {
it.studentName.getUnauthorizedVersion() == normalizedStudentName
}
@@ -43,6 +45,14 @@ class GetMailboxByStudentUseCase @Inject constructor(
return endParts.joinToString(" ")
}
+ private fun String.getReversedName(): String {
+ val parts = normalizeStudentName().split(" ")
+
+ return parts
+ .asReversed()
+ .joinToString(" ")
+ }
+
private fun String.getUnauthorizedVersion(): String {
return normalizeStudentName().split(" ")
.joinToString(" ") {
diff --git a/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt
index 96a84a5a6..029800266 100644
--- a/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/domain/GetMailboxByStudentUseCaseTest.kt
@@ -64,6 +64,18 @@ class GetMailboxByStudentUseCaseTest {
assertEquals(expectedMailbox, selectedMailbox)
}
+ @Test
+ fun `get mailbox for user with reversed name`() = runTest {
+ val student = getStudentEntity(
+ userName = "Kowalski Jan",
+ studentName = "Jan Kowalski",
+ )
+ val expectedMailbox = getMailboxEntity("Kowalski Jan")
+ coEvery { mailboxDao.loadAll(any()) } returns listOf(expectedMailbox)
+
+ assertEquals(expectedMailbox, systemUnderTest(student))
+ }
+
@Test
fun `get mailbox for unique non-authorized student`() = runTest {
val student = getStudentEntity(
From 650cbd5a10f8d6e27242f7306daa546cab7ed4ad Mon Sep 17 00:00:00 2001
From: Damian Czupryn <60961958+Daxxxis@users.noreply.github.com>
Date: Sat, 19 Nov 2022 14:11:26 +0100
Subject: [PATCH 276/669] Add info about optional ads in README (#2054)
---
README.cs.md | 2 +-
README.de.md | 4 ++--
README.en.md | 2 +-
README.md | 2 +-
README.sk.md | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/README.cs.md b/README.cs.md
index 2b0dc12ea..d3d4e2557 100644
--- a/README.cs.md
+++ b/README.cs.md
@@ -34,7 +34,7 @@ Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče
* podpora více účtů s možností přejmenování žáků
* tmavý a černý (AMOLED) motiv
* offline režim
-* žádné reklamy
+* volitelné reklamy na podporu projektu
## Stáhnout
diff --git a/README.de.md b/README.de.md
index 6df10ecd0..853abd13e 100644
--- a/README.de.md
+++ b/README.de.md
@@ -21,7 +21,7 @@ Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre
* Prozentsatz der Anwesenheit
* Prüfungen
* Stundenplan
- * Unterricht abgeschlossen
+ * abgeschlossene Unterrichtsstunden
* Nachrichten
* Hausaufgaben
* Anmerkungen
@@ -34,7 +34,7 @@ Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre
* Unterstützung für mehrere Konten mit der Möglichkeit, den Namen des Schülers zu ändern
* dunkles und schwarzes (AMOLED) Thema
* Offline-Modus
-* keine Werbung
+* optionale Werbungen, die es uns ermöglichen das Projekt zu unterstützen
## Herunterladen
diff --git a/README.en.md b/README.en.md
index 417b74de0..7877bf377 100644
--- a/README.en.md
+++ b/README.en.md
@@ -34,7 +34,7 @@ Unofficial android VULCAN UONET+ register client for both students and their par
* support for multiple accounts with the ability to rename students
* dark and black (AMOLED) theme
* offline mode
-* no ads
+* optional ads which allow to support the project
## Download
diff --git a/README.md b/README.md
index 75b6cfca2..09480e7d7 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
* obsługa wielu kont wraz z możliwością zmiany nazwy ucznia
* ciemny i czarny (AMOLED) motyw
* tryb offline
-* brak reklam
+* opcjonalne reklamy umożliwiające wsparcie projektu
## Pobierz
diff --git a/README.sk.md b/README.sk.md
index 240f8835c..64786556e 100644
--- a/README.sk.md
+++ b/README.sk.md
@@ -34,7 +34,7 @@ Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov
* podpora viacerých účtov s možnosťou premenovania žiakov
* tmavý a čierny (AMOLED) motív
* offline režim
-* žiadne reklamy
+* voliteľné reklamy na podporu projektu
## Stiahnuť
From fdce2cf477cc98d4035431e1419e046d1db6d2d8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 19 Nov 2022 22:50:51 +0100
Subject: [PATCH 277/669] Improve error handling in horizontal tile on
dashboard (#2053)
---
.../wulkanowy/data/mappers/MessageMapper.kt | 10 +-
.../data/repositories/MessageRepository.kt | 14 +-
.../ui/modules/dashboard/DashboardItem.kt | 19 ++-
.../modules/dashboard/DashboardPresenter.kt | 82 ++++++-----
.../dashboard/adapters/DashboardAdapter.kt | 136 ++++++++++--------
.../wulkanowy/utils/ContextExtension.kt | 7 +
app/src/main/res/drawable/ic_error_filled.xml | 9 ++
.../item_dashboard_horizontal_group.xml | 53 ++++++-
8 files changed, 228 insertions(+), 102 deletions(-)
create mode 100644 app/src/main/res/drawable/ic_error_filled.xml
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
index 87111dd4d..120eb183a 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/MessageMapper.kt
@@ -6,12 +6,18 @@ import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
-fun List.mapToEntities(student: Student, mailbox: Mailbox?, allMailboxes: List): List = map {
+fun List.mapToEntities(
+ student: Student,
+ mailbox: Mailbox?,
+ allMailboxes: List
+): List = map {
Message(
messageGlobalKey = it.globalKey,
mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box ->
box.fullName == it.mailbox
- }?.globalKey!!,
+ }?.globalKey.let { mailboxKey ->
+ requireNotNull(mailboxKey) { "Can't find ${it.mailbox} in $allMailboxes" }
+ },
email = student.email,
messageId = it.id,
correspondents = it.correspondents,
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
index f8be4296d..f95b8dbec 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
@@ -3,7 +3,7 @@ package io.github.wulkanowy.data.repositories
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
-import io.github.wulkanowy.data.Resource
+import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.dao.MailboxDao
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
@@ -14,9 +14,7 @@ import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
import io.github.wulkanowy.data.enums.MessageFolder.TRASHED
import io.github.wulkanowy.data.mappers.mapFromEntities
import io.github.wulkanowy.data.mappers.mapToEntities
-import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.data.pojos.MessageDraft
-import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Folder
@@ -194,7 +192,9 @@ class MessageRepository @Inject constructor(
it.isEmpty() || isExpired || forceRefresh
},
query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) },
- fetch = { sdk.init(student).getMailboxes().mapToEntities(student) },
+ fetch = {
+ sdk.init(student).getMailboxes().mapToEntities(student)
+ },
saveFetchResult = { old, new ->
mailboxDao.deleteAll(old uniqueSubtract new)
mailboxDao.insertAll(new uniqueSubtract old)
@@ -207,7 +207,11 @@ class MessageRepository @Inject constructor(
val mailbox = getMailboxByStudentUseCase(student)
return if (mailbox == null) {
- getMailboxes(student, forceRefresh = true).toFirstResult()
+ getMailboxes(student, forceRefresh = true)
+ .onResourceError { throw it }
+ .onResourceSuccess { Timber.i("Found ${it.size} new mailboxes") }
+ .waitForResult()
+
getMailboxByStudentUseCase(student)
} else mailbox
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt
index e220ae236..d019dea68 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardItem.kt
@@ -33,18 +33,27 @@ sealed class DashboardItem(val type: Type) {
}
data class HorizontalGroup(
- val unreadMessagesCount: Int? = null,
- val attendancePercentage: Double? = null,
- val luckyNumber: Int? = null,
+ val unreadMessagesCount: Cell? = null,
+ val attendancePercentage: Cell? = null,
+ val luckyNumber: Cell? = null,
override val error: Throwable? = null,
override val isLoading: Boolean = false
) : DashboardItem(Type.HORIZONTAL_GROUP) {
+ data class Cell(
+ val data: T?,
+ val error: Boolean,
+ val isLoading: Boolean,
+ ) {
+ val isHidden: Boolean
+ get() = data == null && !error && !isLoading
+ }
+
override val isDataLoaded
- get() = unreadMessagesCount != null || attendancePercentage != null || luckyNumber != null
+ get() = unreadMessagesCount?.isLoading == false || attendancePercentage?.isLoading == false || luckyNumber?.isLoading == false
val isFullDataLoaded
- get() = luckyNumber != -1 && attendancePercentage != -1.0 && unreadMessagesCount != -1
+ get() = luckyNumber?.isLoading != true && attendancePercentage?.isLoading != true && unreadMessagesCount?.isLoading != true
}
data class Grades(
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index cb92b0043..22b0d267e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -226,50 +226,71 @@ class DashboardPresenter @Inject constructor(
private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) {
flow {
- val semester = semesterRepository.getCurrentSemester(student)
- val mailbox = messageRepository.getMailboxByStudent(student)
val selectedTiles = preferencesRepository.selectedDashboardTiles
-
val flowSuccess = flowOf(Resource.Success(null))
+
val luckyNumberFlow = luckyNumberRepository.getLuckyNumber(student, forceRefresh)
.mapResourceData {
it ?: LuckyNumber(0, LocalDate.now(), 0)
}
+ .onResourceError { errorHandler.dispatch(it) }
.takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowSuccess
- val messageFLow = messageRepository.getMessages(
- student = student,
- mailbox = mailbox,
- folder = MessageFolder.RECEIVED,
- forceRefresh = forceRefresh
- ).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
+ val messageFLow = flatResourceFlow {
+ val mailbox = messageRepository.getMailboxByStudent(student)
- val attendanceFlow = attendanceSummaryRepository.getAttendanceSummary(
- student = student,
- semester = semester,
- subjectId = -1,
- forceRefresh = forceRefresh
- ).takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess
+ messageRepository.getMessages(
+ student = student,
+ mailbox = mailbox,
+ folder = MessageFolder.RECEIVED,
+ forceRefresh = forceRefresh
+ )
+ }
+ .onResourceError { errorHandler.dispatch(it) }
+ .takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
+
+ val attendanceFlow = flatResourceFlow {
+ val semester = semesterRepository.getCurrentSemester(student)
+ attendanceSummaryRepository.getAttendanceSummary(
+ student = student,
+ semester = semester,
+ subjectId = -1,
+ forceRefresh = forceRefresh
+ )
+ }
+ .onResourceError { errorHandler.dispatch(it) }
+ .takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess
emitAll(
combine(
- luckyNumberFlow,
- messageFLow,
- attendanceFlow
+ flow = luckyNumberFlow,
+ flow2 = messageFLow,
+ flow3 = attendanceFlow,
) { luckyNumberResource, messageResource, attendanceResource ->
val resList = listOf(luckyNumberResource, messageResource, attendanceResource)
- resList.firstNotNullOfOrNull { it.errorOrNull }?.let { throw it }
- val isLoading = resList.any { it is Resource.Loading }
-
- val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber
- val messageCount = messageResource.dataOrNull?.count { it.unread }
- val attendancePercentage = attendanceResource.dataOrNull?.calculatePercentage()
DashboardItem.HorizontalGroup(
- isLoading = isLoading,
- attendancePercentage = if (attendancePercentage == 0.0 && isLoading) -1.0 else attendancePercentage,
- unreadMessagesCount = if (messageCount == 0 && isLoading) -1 else messageCount,
- luckyNumber = if (luckyNumber == 0 && isLoading) -1 else luckyNumber
+ isLoading = resList.any { it is Resource.Loading },
+ error = resList.map { it.errorOrNull }.let { errors ->
+ if (errors.all { it != null }) {
+ errors.firstOrNull()
+ } else null
+ },
+ attendancePercentage = DashboardItem.HorizontalGroup.Cell(
+ data = attendanceResource.dataOrNull?.calculatePercentage(),
+ error = attendanceResource.errorOrNull != null,
+ isLoading = attendanceResource is Resource.Loading,
+ ),
+ unreadMessagesCount = DashboardItem.HorizontalGroup.Cell(
+ data = messageResource.dataOrNull?.count { it.unread },
+ error = messageResource.errorOrNull != null,
+ isLoading = messageResource is Resource.Loading,
+ ),
+ luckyNumber = DashboardItem.HorizontalGroup.Cell(
+ data = luckyNumberResource.dataOrNull?.luckyNumber,
+ error = luckyNumberResource.errorOrNull != null,
+ isLoading = luckyNumberResource is Resource.Loading,
+ )
)
})
}
@@ -280,11 +301,8 @@ class DashboardPresenter @Inject constructor(
if (it.isLoading) {
Timber.i("Loading horizontal group data started")
-
- if (it.isFullDataLoaded) {
- firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP
- }
} else {
+ firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP
Timber.i("Loading horizontal group result: Success")
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt
index a3c423a8b..2c06e45fd 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/adapters/DashboardAdapter.kt
@@ -171,81 +171,105 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter {
+ updateMarginsRelative(
+ end = if (isAttendanceHidden && isMessagesHidden && !isLuckyNumberHidden) {
+ 0
+ } else context.dpToPx(8f).toInt()
+ )
+ }
+ }
+ }
+
+ private fun ItemDashboardHorizontalGroupBinding.bindMessages(
+ item: DashboardItem.HorizontalGroup,
+ isWideErrorShow: Boolean
+ ) {
+ dashboardHorizontalGroupItemMessageError.isVisible = item.unreadMessagesCount?.error == true
+ with(dashboardHorizontalGroupItemMessageValue) {
+ isVisible = item.unreadMessagesCount?.error != true
+ text = item.unreadMessagesCount?.data.toString()
+ }
+ with(dashboardHorizontalGroupItemMessageContainer) {
+ isVisible = item.unreadMessagesCount?.isHidden == false && !isWideErrorShow
+ setOnClickListener { onMessageTileClickListener() }
+ }
+ }
+
+ private fun ItemDashboardHorizontalGroupBinding.bindAttendance(
+ item: DashboardItem.HorizontalGroup,
+ isWideErrorShow: Boolean
+ ) {
+ val attendancePercentage = item.attendancePercentage?.data
val attendanceColor = when {
attendancePercentage == null || attendancePercentage == .0 -> {
- context.getThemeAttrColor(R.attr.colorOnSurface)
+ root.context.getThemeAttrColor(R.attr.colorOnSurface)
}
attendancePercentage <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> {
- context.getThemeAttrColor(R.attr.colorPrimary)
+ root.context.getThemeAttrColor(R.attr.colorPrimary)
}
attendancePercentage <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> {
- context.getThemeAttrColor(R.attr.colorTimetableChange)
+ root.context.getThemeAttrColor(R.attr.colorTimetableChange)
}
- else -> context.getThemeAttrColor(R.attr.colorOnSurface)
+ else -> root.context.getThemeAttrColor(R.attr.colorOnSurface)
}
val attendanceString = if (attendancePercentage == null || attendancePercentage == .0) {
- context.getString(R.string.dashboard_horizontal_group_no_data)
+ root.context.getString(R.string.dashboard_horizontal_group_no_data)
} else {
"%.2f%%".format(attendancePercentage)
}
- with(binding.dashboardHorizontalGroupItemAttendanceValue) {
+ dashboardHorizontalGroupItemAttendanceError.isVisible =
+ item.attendancePercentage?.error == true
+ with(dashboardHorizontalGroupItemAttendanceValue) {
+ isVisible = item.attendancePercentage?.error != true
text = attendanceString
setTextColor(attendanceColor)
}
-
- with(binding) {
- dashboardHorizontalGroupItemMessageValue.text = unreadMessagesCount.toString()
- dashboardHorizontalGroupItemLuckyValue.text = if (luckyNumber == 0) {
- context.getString(R.string.dashboard_horizontal_group_no_data)
- } else luckyNumber?.toString()
-
- dashboardHorizontalGroupItemInfoContainer.isVisible = error != null || isLoadingVisible
- dashboardHorizontalGroupItemInfoProgress.isVisible = isLoadingVisible
- dashboardHorizontalGroupItemInfoErrorText.isVisible = error != null
-
- with(dashboardHorizontalGroupItemLuckyContainer) {
- isVisible = luckyNumber != null && luckyNumber != -1 && !isLoadingVisible
- setOnClickListener { onLuckyNumberTileClickListener() }
-
- updateLayoutParams {
- updateMarginsRelative(
- end = if (attendancePercentage == null && unreadMessagesCount == null && luckyNumber != null) {
- 0
- } else {
- context.dpToPx(8f).toInt()
- }
- )
+ with(dashboardHorizontalGroupItemAttendanceContainer) {
+ isVisible = item.attendancePercentage?.isHidden == false && !isWideErrorShow
+ setOnClickListener { onAttendanceTileClickListener() }
+ updateLayoutParams {
+ matchConstraintPercentWidth = when {
+ item.luckyNumber?.isHidden == true && item.unreadMessagesCount?.isHidden == true -> 1.0f
+ item.luckyNumber?.isHidden == true || item.unreadMessagesCount?.isHidden == true -> 0.5f
+ else -> 0.4f
}
}
-
- with(dashboardHorizontalGroupItemAttendanceContainer) {
- isVisible =
- attendancePercentage != null && attendancePercentage != -1.0 && !isLoadingVisible
- updateLayoutParams {
- matchConstraintPercentWidth = when {
- luckyNumber == null && unreadMessagesCount == null -> 1.0f
- luckyNumber == null || unreadMessagesCount == null -> 0.5f
- else -> 0.4f
- }
- }
- setOnClickListener { onAttendanceTileClickListener() }
- }
-
- with(dashboardHorizontalGroupItemMessageContainer) {
- isVisible =
- unreadMessagesCount != null && unreadMessagesCount != -1 && !isLoadingVisible
- setOnClickListener { onMessageTileClickListener() }
- }
}
}
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 dd91d36d4..cc4c5aaa4 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/ContextExtension.kt
@@ -2,9 +2,11 @@ package io.github.wulkanowy.utils
import android.annotation.SuppressLint
import android.content.Context
+import android.content.res.ColorStateList
import android.graphics.*
import android.text.TextPaint
import android.util.DisplayMetrics.DENSITY_DEFAULT
+import android.widget.ImageView
import androidx.annotation.*
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
@@ -12,6 +14,7 @@ import androidx.core.graphics.applyCanvas
import androidx.core.graphics.drawable.RoundedBitmapDrawable
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import androidx.core.graphics.drawable.toBitmap
+import androidx.core.widget.ImageViewCompat
@ColorInt
@@ -85,3 +88,7 @@ fun Context.createNameInitialsDrawable(
return RoundedBitmapDrawableFactory.create(this.resources, bitmap)
.apply { isCircular = true }
}
+
+fun ImageView.setTint(@ColorInt color: Int) {
+ ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(color))
+}
diff --git a/app/src/main/res/drawable/ic_error_filled.xml b/app/src/main/res/drawable/ic_error_filled.xml
new file mode 100644
index 000000000..61b575dc6
--- /dev/null
+++ b/app/src/main/res/drawable/ic_error_filled.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/item_dashboard_horizontal_group.xml b/app/src/main/res/layout/item_dashboard_horizontal_group.xml
index 1d43d5115..0c59d1ebf 100644
--- a/app/src/main/res/layout/item_dashboard_horizontal_group.xml
+++ b/app/src/main/res/layout/item_dashboard_horizontal_group.xml
@@ -37,9 +37,25 @@
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
+ app:layout_goneMarginEnd="16dp"
app:tint="?colorOnSurface"
tools:ignore="ContentDescription" />
+
+
+
+
+ tools:text="16"
+ tools:visibility="visible" />
@@ -145,9 +178,25 @@
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
+ app:layout_goneMarginEnd="16dp"
app:tint="?colorOnSurface"
tools:ignore="ContentDescription" />
+
+
-
\ No newline at end of file
+
From 9c60ce688b48226e7ffe44f5029a89de8744f7b0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sun, 20 Nov 2022 00:05:34 +0100
Subject: [PATCH 278/669] Version 1.8.1
---
app/build.gradle | 8 ++++----
app/src/main/play/release-notes/pl-PL/default.txt | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 62a5a21fb..f6a82ad20 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,8 +23,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 32
- versionCode 115
- versionName "1.8.0"
+ versionCode 116
+ versionName "1.8.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -161,7 +161,7 @@ play {
defaultToAppBundles = false
track = 'production'
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
- userFraction = 0.25d
+ userFraction = 0.10d
updatePriority = 4
enabled.set(false)
}
@@ -186,7 +186,7 @@ ext {
}
dependencies {
- implementation "io.github.wulkanowy:sdk:1.8.0"
+ implementation "io.github.wulkanowy:sdk:1.8.1"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
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 996d5eebc..7b2fda861 100644
--- a/app/src/main/play/release-notes/pl-PL/default.txt
+++ b/app/src/main/play/release-notes/pl-PL/default.txt
@@ -1,4 +1,4 @@
-Wersja 1.8.0
+Wersja 1.8.1
- naprawiliśmy liczenie średniej ucznia w ocenach klasy dla wykresu "Wszystkie"
- zmieniliśmy kolejność przycisków akcji w podglądzie wiadomości
From 49e68f5c8bdc47d276268bbd4100d47be1490112 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Nov 2022 20:50:47 +0000
Subject: [PATCH 279/669] Bump agconnect-crash from 1.7.3.300 to 1.7.3.302
(#2060)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index f6a82ad20..a7c09c568 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -250,7 +250,7 @@ dependencies {
playImplementation 'com.google.android.gms:play-services-ads:21.3.0'
hmsImplementation 'com.huawei.hms:hianalytics:6.8.0.300'
- hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.3.300'
+ hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.3.302'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
From 890d60811b9e67dd905feff4366724474dc7065c Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Nov 2022 20:51:07 +0000
Subject: [PATCH 280/669] Bump agcp from 1.7.3.301 to 1.7.3.302 (#2059)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index e8e1052b6..d09063fb6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.google.gms:google-services:4.3.14'
- classpath 'com.huawei.agconnect:agcp:1.7.3.301'
+ classpath 'com.huawei.agconnect:agcp:1.7.3.302'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
classpath "com.github.triplet.gradle:play-publisher:3.6.0"
classpath "ru.cian:huawei-publish-gradle-plugin:1.3.4"
From 85ce23845ff9498f11d053dea3babbcc17711ef6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 21 Nov 2022 20:51:23 +0000
Subject: [PATCH 281/669] Bump firebase-bom from 31.0.3 to 31.1.0 (#2057)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index a7c09c568..de3b49134 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -241,7 +241,7 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
- playImplementation platform('com.google.firebase:firebase-bom:31.0.3')
+ playImplementation platform('com.google.firebase:firebase-bom:31.1.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From ae39bd94e5815b6807b60b8cbce366595b88dec3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 22 Nov 2022 20:42:38 +0000
Subject: [PATCH 282/669] Bump hilt_version from 2.44.1 to 2.44.2 (#2058)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index d09063fb6..487e5f6e3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ buildscript {
ext {
kotlin_version = '1.7.21'
about_libraries = '10.5.1'
- hilt_version = "2.44.1"
+ hilt_version = "2.44.2"
}
repositories {
mavenCentral()
From 9dc12204961c1a90df941f39136a4ba7e311ce60 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 28 Nov 2022 18:36:14 +0000
Subject: [PATCH 283/669] Bump about_libraries from 10.5.1 to 10.5.2 (#2066)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 487e5f6e3..b219c8318 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
buildscript {
ext {
kotlin_version = '1.7.21'
- about_libraries = '10.5.1'
+ about_libraries = '10.5.2'
hilt_version = "2.44.2"
}
repositories {
From 8f50ee82b3275d92fd45af6948e25287b02156d0 Mon Sep 17 00:00:00 2001
From: Patryk <43276401+Zaptyp@users.noreply.github.com>
Date: Mon, 28 Nov 2022 19:50:14 +0100
Subject: [PATCH 284/669] Change fakelog to https (#2063)
---
app/src/main/res/values/api_hosts.xml | 2 +-
app/src/main/res/xml/network_security_config.xml | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/app/src/main/res/values/api_hosts.xml b/app/src/main/res/values/api_hosts.xml
index 8413d68e4..e7373e114 100644
--- a/app/src/main/res/values/api_hosts.xml
+++ b/app/src/main/res/values/api_hosts.xml
@@ -40,7 +40,7 @@
- https://vulcan.net.pl/?login
- https://vulcan.net.pl/?login
- https://vulcan.net.pl/?login
- - http://fakelog.cf/?email
+ - https://fakelog.cf/?email
- Default
diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml
index 84ff05a04..17fac4d1e 100644
--- a/app/src/main/res/xml/network_security_config.xml
+++ b/app/src/main/res/xml/network_security_config.xml
@@ -1,6 +1,5 @@
-
From 302d723cfbba95279142dd04197766f863b61e4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Thu, 1 Dec 2022 18:14:28 +0100
Subject: [PATCH 285/669] Suppress menu deprecations (#2031)
---
.../github/wulkanowy/ui/modules/account/AccountFragment.kt | 1 +
.../modules/account/accountdetails/AccountDetailsFragment.kt | 1 +
.../wulkanowy/ui/modules/attendance/AttendanceFragment.kt | 1 +
.../wulkanowy/ui/modules/dashboard/DashboardFragment.kt | 1 +
.../ui/modules/debug/logviewer/LogViewerFragment.kt | 5 ++---
.../io/github/wulkanowy/ui/modules/grade/GradeFragment.kt | 1 +
.../ui/modules/grade/details/GradeDetailsFragment.kt | 5 ++---
.../ui/modules/message/preview/MessagePreviewFragment.kt | 1 +
.../wulkanowy/ui/modules/message/tab/MessageTabFragment.kt | 2 ++
.../wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt | 1 +
.../wulkanowy/ui/modules/timetable/TimetableFragment.kt | 1 +
11 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt
index 051c93c95..f115372a5 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/AccountFragment.kt
@@ -34,6 +34,7 @@ class AccountFragment : BaseFragment(R.layout.fragment_a
override val titleStringId = R.string.account_title
+ @Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt
index c3137ec58..c6fe8a69b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/account/accountdetails/AccountDetailsFragment.kt
@@ -43,6 +43,7 @@ class AccountDetailsFragment :
}
}
+ @Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
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 6354b5e04..21f30b046 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
@@ -84,6 +84,7 @@ class AttendanceFragment : BaseFragment(R.layout.frag
}
}
+ @Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
index de0b4a6c9..cd66d6c2f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardFragment.kt
@@ -61,6 +61,7 @@ class DashboardFragment : BaseFragment(R.layout.fragme
fun newInstance() = DashboardFragment()
}
+ @Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt
index 1e11c874b..929e72645 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/debug/logviewer/LogViewerFragment.kt
@@ -1,9 +1,7 @@
package io.github.wulkanowy.ui.modules.debug.logviewer
import android.content.Intent
-import android.content.Intent.EXTRA_EMAIL
-import android.content.Intent.EXTRA_STREAM
-import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
+import android.content.Intent.*
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
@@ -36,6 +34,7 @@ class LogViewerFragment : BaseFragment(R.layout.fragme
fun newInstance() = LogViewerFragment()
}
+ @Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
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 0a8561eec..15df47a19 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
@@ -51,6 +51,7 @@ class GradeFragment : BaseFragment(R.layout.fragment_grade
override val currentPageIndex get() = binding.gradeViewPager.currentItem
+ @Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
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 81f3226ad..23d767a6f 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
@@ -5,9 +5,7 @@ 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.INVISIBLE
-import android.view.View.VISIBLE
+import android.view.View.*
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
@@ -42,6 +40,7 @@ class GradeDetailsFragment :
override val isViewEmpty
get() = gradeDetailsAdapter.itemCount == 0
+ @Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
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 2a5523f4d..8c6b0402b 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
@@ -73,6 +73,7 @@ class MessagePreviewFragment :
}
}
+ @Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
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 5d608ad3b..eddb43243 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
@@ -86,6 +86,7 @@ class MessageTabFragment : BaseFragment(R.layout.frag
}
}
+ @Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
@@ -135,6 +136,7 @@ class MessageTabFragment : BaseFragment(R.layout.frag
}
}
+ @Suppress("DEPRECATION")
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.action_menu_message_tab, menu)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt
index 361a59440..6ff7a39b7 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/studentinfo/StudentInfoFragment.kt
@@ -65,6 +65,7 @@ class StudentInfoFragment :
}
}
+ @Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
index fdd4aface..6fd126326 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt
@@ -51,6 +51,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme
override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize
+ @Suppress("DEPRECATION")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
From 429fdfa4a0380bb520e116e0112c01c524f5de1a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Thu, 1 Dec 2022 19:02:25 +0100
Subject: [PATCH 286/669] Update project to Android SDK 33 (#2011)
---
app/build.gradle | 14 ++--
.../res/mipmap-anydpi-v26/ic_launcher.xml | 3 +-
.../mipmap-anydpi-v26/ic_launcher_round.xml | 5 --
.../res/mipmap-hdpi/ic_launcher_round.png | Bin 4369 -> 0 bytes
.../res/mipmap-mdpi/ic_launcher_round.png | Bin 2798 -> 0 bytes
.../res/mipmap-xhdpi/ic_launcher_round.png | Bin 6193 -> 0 bytes
.../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 9606 -> 0 bytes
.../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 13447 -> 0 bytes
app/src/main/AndroidManifest.xml | 8 +-
.../github/wulkanowy/ui/base/ErrorDialog.kt | 2 +-
.../github/wulkanowy/ui/base/ThemeManager.kt | 18 ++++-
.../accountdetails/AccountDetailsFragment.kt | 11 +--
.../account/accountedit/AccountEditDialog.kt | 13 ++--
.../accountquick/AccountQuickDialog.kt | 10 +--
.../ui/modules/attendance/AttendanceDialog.kt | 8 +-
.../ui/modules/conference/ConferenceDialog.kt | 10 +--
.../wulkanowy/ui/modules/exam/ExamDialog.kt | 8 +-
.../grade/details/GradeDetailsDialog.kt | 20 +++--
.../statistics/GradeStatisticsFragment.kt | 5 +-
.../homework/details/HomeworkDetailsDialog.kt | 8 +-
.../ui/modules/login/LoginActivity.kt | 27 ++++++-
.../login/recover/LoginRecoverFragment.kt | 2 +-
.../LoginStudentSelectFragment.kt | 9 +--
.../LoginStudentSelectPresenter.kt | 2 +-
.../studentselect/LoginStudentSelectView.kt | 2 +-
.../login/symbol/LoginSymbolFragment.kt | 8 +-
.../wulkanowy/ui/modules/main/MainActivity.kt | 13 +++-
.../ui/modules/main/MainPresenter.kt | 7 +-
.../mailboxchooser/MailboxChooserDialog.kt | 4 +-
.../message/preview/MessagePreviewFragment.kt | 12 +--
.../message/send/SendMessageActivity.kt | 9 ++-
.../modules/message/tab/MessageTabFragment.kt | 12 ++-
.../wulkanowy/ui/modules/note/NoteDialog.kt | 10 +--
.../notifications/NotificationsFragment.kt | 64 ++++++++++++++++
.../SchoolAnnouncementDialog.kt | 10 +--
.../notifications/NotificationsFragment.kt | 63 ++++++++++------
.../notifications/NotificationsPresenter.kt | 32 ++++++--
.../notifications/NotificationsView.kt | 10 ++-
.../studentinfo/StudentInfoFragment.kt | 22 +++---
.../ui/modules/timetable/TimetableDialog.kt | 14 ++--
.../ui/modules/timetable/TimetableFragment.kt | 5 +-
.../completed/CompletedLessonDialog.kt | 10 +--
.../github/wulkanowy/utils/BundleExtension.kt | 32 ++++++++
.../io/github/wulkanowy/utils/IntentUtils.kt | 21 ++++++
.../ic_launcher_foreground_dev_mono.xml | 20 +++++
.../drawable/ic_launcher_foreground_mono.xml | 12 +++
.../res/layout/fragment_notifications.xml | 69 ++++++++++++++++++
.../res/mipmap-anydpi-v26/ic_launcher.xml | 3 +-
.../mipmap-anydpi-v26/ic_launcher_round.xml | 5 --
.../res/mipmap-hdpi/ic_launcher_round.png | Bin 4025 -> 0 bytes
.../res/mipmap-mdpi/ic_launcher_round.png | Bin 2569 -> 0 bytes
.../res/mipmap-xhdpi/ic_launcher_round.png | Bin 5740 -> 0 bytes
.../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 8796 -> 0 bytes
.../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 12531 -> 0 bytes
app/src/main/res/values/strings.xml | 5 ++
.../main/res/xml/data_extraction_rules.xml | 17 +++++
.../LoginStudentSelectPresenterTest.kt | 4 +-
57 files changed, 496 insertions(+), 182 deletions(-)
delete mode 100644 app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
delete mode 100644 app/src/debug/res/mipmap-hdpi/ic_launcher_round.png
delete mode 100644 app/src/debug/res/mipmap-mdpi/ic_launcher_round.png
delete mode 100644 app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png
delete mode 100644 app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png
delete mode 100644 app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png
create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/notifications/NotificationsFragment.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt
create mode 100644 app/src/main/res/drawable/ic_launcher_foreground_dev_mono.xml
create mode 100644 app/src/main/res/drawable/ic_launcher_foreground_mono.xml
create mode 100644 app/src/main/res/layout/fragment_notifications.xml
delete mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
delete mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.png
delete mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.png
delete mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
delete mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
delete mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
create mode 100644 app/src/main/res/xml/data_extraction_rules.xml
diff --git a/app/build.gradle b/app/build.gradle
index de3b49134..cf7223bae 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -16,13 +16,13 @@ apply from: 'hooks.gradle'
android {
namespace 'io.github.wulkanowy'
- compileSdkVersion 32
+ compileSdkVersion 33
defaultConfig {
applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
- targetSdkVersion 32
+ targetSdkVersion 33
versionCode 116
versionName "1.8.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -193,9 +193,9 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
- implementation "androidx.core:core-ktx:1.8.0"
+ implementation "androidx.core:core-ktx:1.9.0"
implementation 'androidx.core:core-splashscreen:1.0.0'
- implementation "androidx.activity:activity-ktx:1.5.1"
+ implementation "androidx.activity:activity-ktx:1.6.1"
implementation "androidx.appcompat:appcompat:1.5.1"
implementation "androidx.fragment:fragment-ktx:1.5.4"
implementation "androidx.annotation:annotation:1.5.0"
@@ -271,9 +271,9 @@ dependencies {
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
- androidTestImplementation "androidx.test:core:1.4.0"
- androidTestImplementation "androidx.test:runner:1.4.0"
- androidTestImplementation "androidx.test.ext:junit:1.1.3"
+ androidTestImplementation "androidx.test:core:1.5.0"
+ androidTestImplementation "androidx.test:runner:1.5.1"
+ androidTestImplementation "androidx.test.ext:junit:1.1.4"
androidTestImplementation "io.mockk:mockk-android:$mockk"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
}
diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
index 7dbec2cb9..b7b756b9e 100644
--- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -2,4 +2,5 @@
-
\ No newline at end of file
+
+
diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 7dbec2cb9..000000000
--- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 81e723eccf63eb93c41506650615abaf157a8f93..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 4369
zcmeAS@N?(olHy`uVBq!ia0y~yVDJE84mJh`hS0a0-5D4JZg{#lhE&{o8p9b7{9LRr
zYR%Oyg_YciywPQgG_vk4So=Hp^|j{~cC}&FR$?o|0++1MdmOwv`FZA6yXf*OVPcG~
zf-a1Vg^a44>ogXXHa}wOSfujr{hZm=hkZRFoP@UO8>jz!vhM%Scjecr^DX9>|4@|i
zB(_vF{yd-vYoFD7sx)Kg`h
zLfn=ekBnl{&l%qD95pHCYZ`wOw~9oon#|w?ww;FEd=2*Ie+@ji~Um
z*uK$nZy@`b4Q`5)E1AA7zIi|*uGE`b&iX!s#vJBzM+@rA{y$-3x$!bWd2%iDfjt46
z)N*|jd>D_;5q=ibTWn+&KD&uInVms(;?WsW(Tz!7Gb}HNzvei1ahZalI+K+5120kS
zm!6S__D4(zlw#AEle{iOYb&Qm&*SFF{EkxE_l2&RoKTy&=GPn_vn#94%|1Hkqt-Ox
zy6A&}66XTdlr4o$-d}r276-E<^i(l^Vi@1-o-6k-{Q
zy#>3UiW!=#wQIb&dV1c|3HL1vcpRCQ*IEZ^m8QJDq0{i`Wk8;HJI_<*z;7=cr`+BB
z`dCvV^U19nlRX{(|8r5CZhpEj-D|0*%r5b(I+vNe+HMKSg#0^qY3Y)Z^Kze_{3r|(
zllGpcq8|Ks^>Y7Cx5bK~M~^N^cy#31oaJm^l#aVDVojSV75pZ4%4u7MInRCa5`7c&
z>VIkO{CG8diH-H_6{}ZkD!iI}n;~M`w?i|hswZx+)ShbBFUVDU!^)gf!Qv-l_Gb38
z&yRh1xx`4ne?|Ph903WpRbE~maTOLP87~{(zS7|OQt6^2-`$rvvobxGGKZARP4<0e
zb7$Aqiwh)9b-VX_2>np~pq@5Y`b#x0-|QnCEY4E7Sv{9`y?8%A>FtV85h16zJNt6Q
zEcCUtPrg0kB6NSVdZ%!=r`ZO@`)5>)e0A(@YP6rd@Mh2c`UPE~n$brk4^OS!fhe
zzuoUWY`A62JbX>fz1$hhTs3;uTt6dGHTTuEx5^IN&fJ=~y5N+^qLnK%O$=UzuRrnm
zQc%!`%l;~2f7=)>awhnhAG|Z8qby3N?o0Fb?cFDzOmuwL{^M6&
z?j+YOZ?`t^KF$pd65)|>c-Qpk>i758jaV5@NAHVyUBl=yrP<;qn}gw$3X|<_O-Ei-
z`KVZ(GQ9RBJ@2wh=TX^%a`HEn46ig^*{)wXYocS80{bdGef4^WZ#6F-_s{0>sC{-V
z!uYgEx3|)rySq0TUhIy0nN+yp65G!H#6=N1awILq1s(amzfKoScxCaUH?Vq8Q;++2f3jds4Jb@46tJq>n7M6GOz+p96Z*xq)oOsmk6
zoeD=U+pT0gzINY&ug=~UZRU!ZY+G%Vr@Gx*bibH$O5L&m0alTv=`&VK%lPQ6jrCsl
z_Ll2AMuQoz7x)LQiO&}hL=eZXH=Zkda{VKG0TiQ3vv+G)AW$CHu
zC%x8)9$d#G@8@-(CeBf<$Mb&cRPEBqb{~(5TTM1nJFmdo!Cs)tJ87dwnU{{<+=Gv=
zs`cW|iZ_^G7+tl}sor#~?6`r1%?XFqJ
zBk}FWr_{*|53RgiOoCH6gLkBPFFh~5W5S03SbRWA3$S?#sq)>$zT8xLt!3N2&EY1sGlwqTBNksZRS!HFO4e-lOM4*DOK|=Uo7Wf;_>#Jhf3r$9}(^a+g6=9
zyY?+hSm?wnhRG*9IbQ2)Ykd*Xzp-QDtIto)%=e!sCN?YYL9@P-@KzIti%gAD$93=B
zOK0S+-kZMV<(6N}3$ELFGnM4tvH0}poUHA#l=hYY*UXnYi>tb(&$C~z9oFyoEM`Nd
z@KswAwo5A|jn^;*nDo5<@kio_)B2~5;%RI7fB*b@deYHXx*QL)K1lD1&4!=uN4w*IO^=L49<_%NmYW~YV
zpS|*SjW9aq)9bs{FF~{E9TmS+Yi}no7SjTlY-!k(y|E!}Er=
zz=TMfd*U5OjRX$2MV@}h$MHgu4MN
zHlhDPTJ8|Rti7UzEd`J=_m`bsnBT?S*rJ;(bdYL=fA(wcs-sr3H7+FhP?Y-*+z
z9}ltKy$3S?F7em)V-+Pkp9Xv%}Sw%-arD(_{
zF;8gqfi(qt7xu{8&+BQR#WTAD{Kxa6*3U?y_DN7ncbR
zGlJ_4DuoqR3Oda65$oO6y;b5$_3NE)ljPp#i*IZ&UR=5+<)7T!EDV~DJrfVk
zmt^|#dj0-I@(Q-q-&Bm#CY)cr(MQbQRx%9Xl+t_AdPL>T0)(yL&Lh<9!)=o72uJ
z`6tLZD=vuV&}3=FNA(DeY4fumQYrc
z^CCcNPx!^J(=;0betmiAylK;>6aEv}AAbME;53t8vcl1kQPQr);w7VC=I?KBRcvi#
zeciYB3Qo9UQTG0xt#HY^SGkY=nj~LbQ)|(0uDG64C#X^A&92qwnAs==KgZpGr{{W+aWoNhX}?6#`f5_dM$ORJG_
zhNSudmiG-tyfY>A4tpG72$|oxu%)$iqO^J51k>zm0X$9$J-O^^OAm50yw=_klV{+5
zdrAJS;;#Q!ly0Rqw;wmE;quNrB)&31sJ~BU<@Sq5^*m+@De|t%Q%O0xY|fW0nU_oa
zc5P%xd~zsbS9MaGFK1CFs+H~wc52~M@_=H=5@M%cEq?$lYjhtdQD)p_=3Kb
zD;3U2ZPV-F{8n$Y%>Vl8#=1GS(fkWkRvf<2o^x-{%$WUkb8~KNXiO4f7O;9dVS<3|
zL4orc{rl8aRa-+2)GYIvnZz|mN~X59*1sVyE@o}tv%Q|a;yHbD-@N%V*>%zEV{w(I
z+O;Qg?mT7a!LZRk*-Pl4p_JH+?nX@>hZokDE?qj&DXi|1lcO_}_rXSAmRdeH)oqjA
zW?B?3lH>UN?CfG6@z2W2JgW`{stAeRym^y#L0(Bx#=rC>n~S3*ton;eWG1SKg|Fyo
z`Nyqr-0|lPwXlob2M-!CEOe5HHCQF#CZ~4Yu}y+Y_~?yub7gNuE;<&dG%>`=(z4fW
z?wqZ=O#d6NNp`Q6hL6uHX;mqPLHcv@&Oe-&Ij_J+#d`;SqPEVb7WN`^66|
z!~|zZ_n0pHP`fKmm!=&pONAu_`He67f)8Cdz9o`?LDDZ)_g#2#E08|7!)iP;%b1
zcRyy{WLdrN_ZiV->r$s@AC7+eCTkg1b8&lW!tZZC&a=-`-f<_8v#!o{M(%0@_oSzu
z&t}YaJ;=E2zzRzRkxBAD|17HW^Ghx`$Mp8j+J9mtr|oX7esJ&FxpmLw^50Ln%)oZ~
zNu065%*Q)sbDqgg;hk69KVLrb-Me{QoPG-DuHHJc^6_LQ8QG7yNpc-2t-F8k4{kZM
z{6~wJv_|wyX=#a+vtsh|mmANWd;P$j52Lz3phbTy0~hbKS$w
z!v38{udMFZdL}P^;7icjBPu4Ud!z-uf6tkFVAlJ)cl#NlKd{Ed>xH~HXW)KL`k(jQ
z?-{qtm<9dcUP`@{CqJL3r=)VLLuQbQP$`cVOZaX}pTx#Ri4vRdUJu`V>9TS16L)#v
zdQ~sS68{oigL+6V`vu8k)7P)WLmnIk0t-7
zTV~^B;k!ju)_2sW?XzV*y)xQl_TIhnb*VmCj6V
zvt^l7zy11f_yI@w_4nmf)#~b8Eai_KcK%%;qb2FA9W(#J_p>W&%bGd=zIj|+zO?=L
zMoF7{-`^GF%rdMoQP1aNpS-{P(f*dG#D%X{rcTl3u-m9wntOTK|0n0>rnl_eu_pS}
zJGtH0E*qyWo__u7|6f1u+<*4^cSigAd`qsn`&_3BWb>MDx9!-qPWtTAb1%4#FSyxs
zyyS54h4Ui&tm`)&@#B23YF|I6T5jEsH}l0i7#7Qm^xSo*T6C$Y>@A!3+qdltAC$+i
zy{>v-wnKL>|GU>sX)VW(Bp2U%y8opS3wO>!QzPzTA%nesZ!PBN-QZr<=&b(UuUmTW
z8tuI1+mGx{h}ay9=bTa{Ypo^4GLcPYkL;#{&4)9ao9-7zPB7!zS~HpNdwt3CSSQsG
Sp4|)#3=E#GelF{r5}E)q0tuu5
diff --git a/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 394b5707650277a585ca2ea7cd3bf0d446e86a33..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 2798
zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-4mJh`hH$2z?F|^jp7Ut372Yf
z3GASLi#$xU|MQ`hlD
zp^I2Pay)2k>QduqQfS`6je&Lecp1`|sv=lerHc;Bi_0YW?-gfBW87|NmKa
z?BvS1@Jouv$IQ{tg}A-?daPi&|yS19}b@Jv)-t6q{(tozz+q<8o
zU~y96;=K!^h30vyDXm+0z5ZsN(5c25ALh4-cbi5SHkNUpKUsYG{^y5p_$CLqG5g5m
zPbi!FcKhN+bLC4flpk6Bw7Scvep39FkJC$=@UOq#wy90t#}$}&Ep@J5nA%0PPvf@pI77t(ER2RYDv*P@xGck*6Z`>;NJh6MBSbOP}9ENQb
zHw<=dO=h?K^HP1)*^>)zou2+Ou+Y$U?V2UCXPXBz{@Rk|m*Z8n*U(vj?Zz2luifmg
zb8aTe8Z}N3`1RiWen4!U-Ao^~)j_{c>rb&R)mncsszt~!h4WCVI_Ki6J-VyenV)I;
z9DkP)EF$Ig-G81+va@sdgVdcf=lCpVZB*_%`{Ky55QbnYaS4gct5Z_k0?tR@_$y>-
zc6;yd4X;nFjXwF~%Szt|yyxd#JUd0iOWL@NkDV>3N?YZKP2;yo3RCi~X6!P(+;m1G
zTp;bl?)M!pW28+wvNmtdZ8Ep3_j}seS+XYH-ixV1Ozq&T4XJ)>Sbs1rWo0>2>B)WS
zLW%2g@9h4!n;(be-BfCt!g&4q)j(01AJ>j{fB8I7dCLAOQ`xtdk2A$-rsrfh9h)L4
zF=O2a)^zWt6FeLr8h>69-R8`^NQ{k7V55Mb
z3-(OamNi=5q&D;CqOfSY_@%G@Eh_o;^=+$5;cY=C^JD$X**gq)`{jdG9pqo%4v+a`
z<;9U|lC*R3boZ6cZ8v@0wRxw~_=494{Iv<{;{&PGm
z|GzOPx<=2!nk
zFm%b%i+c|_c{v6dFG$GFax+y`{o?%pU)kgYOHqjotqB}1t3#r=8}s+>4dw6;{U&5^
zK=bwLRW1pl>E|xA`r9vE{{3C2b#2`>osZR>=pFXY-eKf$?+3seRxi&
zynJixfg1De?{@N86u#V&d8HxcXzOgoWg#2N*0N^YJlH&?=hpsj&(5Z)M0o3_CPz==
zV9N1p|MT~>`i@=yJR{GSx+(p>7{?@5n2`S_Q=Pjvfs!coA3p|SVgz2A*D
zU61HZ6*}i$TdS?4Pr)m`|G|>9XV6%GE0t
ztXZQ|cyQ{8U6KZ?Q|9andnm~~*QU~F=>vtyvu0_XIycukQ|Z&OdA8LqMK*d;|2Ab@
zRARd`#f5qM-(O!(vxJ%NWT`sI|83<}?wt`?XM5_R_s8|v88NQ=ZdqCQQ%CqlkIV)U
z&+hf#-{03~ntk$L!559z1x{0!+MjuRq*K_0SFqP}-l1bYGmTvL=udXYcFUG)xue9v
zG~0i9!PAXFcJnx6ckfb`ntIDB=-v#2#(Bz_nun8AC*3U6S#qVpr@_(h=)=DpGIhs#
zBuj+)Y%?#Z1Xf)C`IAkgVU6m$<5EcKx-NfzpYAn_)Q=y-IC~ENeDLGrV-wlB$;`P&4~9piDc<~8
zoF`m(CWepw{*J=O-cnqxpEt2Bxf_2};_%wN@4;l
z`Y#=IbL{7zTd?8u0Y7t9gZz6oUF?>T!d?wJhXc%)E??gL@6fUt|>)v=pz{IU)WdEBj^Dmc5sgkDE;nxv_GNuKB__X-uCNPQ1Ij{BqP3ah^Gi
z1r;g64}a_U1hAdHnDOz^(JSJQe^$@8tM$n^doUt=ZP@x`pNDJrMosbmCD^O=O7i_<
z;raJHn)-{9R4c@ePFY*BpZ9OO*F|Q--7|L@INU1~lU(y@Cet2H-JidHsTqi!%$f7H
za__lSpMKptt{!=bWqwbOPgC~i!@2?wrtFv7E!KQA`GQ;G8!hXUrE&RB3{My8%v~I|
zoT=G7xijPByXc>di;Z1+{}d?j3yXR7^=@vqTrBEq5ui2uqS=RIG9SJ#-@iI!&u6J)
z0!wZF-}>A+X^zyRoi}e+$4(3T?Q`bHpDJb-fwgDat@$jrZ@GT{{mpQ#tM(OV%NoOH
z3HRC6Cs@z9yZHE)6#M-)*V?{Jp8Ru*f{BXhw4G_z4YT=8`!+JTFZH~iWm5V0UTwM6
z`lH#ma?Q5MSbMEb75by5kT7?8cuAClL#AHG2LCyeYaR*o)x>PRE%dH9!s@x2=d(?*
zi#21PyxS*L%>GGg&$aiT%v)|8lQ^UH^?7+rOw(dpn`x>)f2E~y9crH3`|QId#~95n
zXHHdz1TFr6>*;!Lu4YS~KU)0h&c^-6`S1E}Rn)yCdWrdw`Ks#ghK6!-+S^uK&^Evqx23p1-n6X_$T6Lu}u(>OVfOE;~yp&s!CzS+M#aqd@0|=#%~_Z$JE(|EF>C
m_9a{KcgkZ9tK>0JyA3=E#GelF{r5}E*`xJ=Ig
diff --git a/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index 365b4d663a8eae4600393c92446ebcbb629b0d1b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 6193
zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^TYe|Wk$hE&{o8_O9H@>ELB
zP{bwBGk|4cVwyx6>ycW`=&PqTo!J&0R<%~{b(;C^)YN0T`PyfOqp=32
zBGcenx+xXSyrpcz-l6EK?+EI~@%(Yz
zy6a}$;=+V5pEET~%MPgUmT5+Ip7^kHN>s(n8!cajZbwaQx_>DD(e^^VZRg(mbJt8e
zD3I9I*{R-nVj@EK6=kI99o^mGhP8SuD4g6>K`kHQ%>tZ}CBRQ27R7cxhKBJ-QKN>pUU>iW7pj@WhL^s?J)=T7DhZ-^6>
zOj`MqOO7+OovUD0j8?XewJ(_4G_v?C<10)VUz^!pVPGyJ|P7eig0o
zJ}pprc;AZ`mLLC^?w-q{cw^Cwg_FIFK1xN&Z`o(IvC-I=^Q5$r(yVU7g6Pxt#f2|7
z`DwLY|2^rKj^(#yYZVgLU))i>*H8R`n7=?>fpyfri#tw#QJ*YRt-`7D#!w=tj*~zC
zVUSP@Ps8;;U(2O_Oe<78n&45_JY8{ly89y;i^~~vYSmBpO^uaOIGB-Q{pHtAr?Mwk
ztQ-?84kt+Ha_uOW`H>{#({j*GDRFa{^!pHffkn24_rkACe75tZUyjacCF9c9$N2Vs
zU^*>p*7Nkjr7L@PKi;fdY?E*@>+58{lRYgAlbuv8|2`=6KlRzywray2^Lq)bmzQn)
z%*pxY&ZDC`^?xeL^8VG7m7S9*Q#71-*Q$EjR59_*v$xo|&);12wW!2KgH_G?>o4Kr
zsiy+k*;4D=n7*uK-1?7IPPimF>Bhr@&5?nAeA?Q&si~={Ws#YloYr67ZqHdSyi3nW
z!PV++kfgo#Va2)SOp{+ed+1bX_eSpS+bf+<_x`uD-k&46d*=Ln^AAbZyRt-YdtBPm
z&QP3TvmjN<^Wv=&Cl2y`WKvu(VVSAAxb+!t|CPI=YR)Zid~>Epa?O$zE5Z(Nht>SG
zIvu;EBX`$(yY{Lk1=rhOf8Cj{q#S+KQsc9=`HgIYn#3aYA1TM(dXtuxvh15AGBIaq
zuiV*duS#_8PMqf;@v!2x$*e-7x?R3=qyC#UD>m^}O*~!2!?r!e_U8Bd=k;{r+9L3JoYO{e|?|-gXd;h
z&VRk#;xjWJxolhHE#Aj)!|D?Y`>BaHxfd;3c2eiq(QJ_lb9dL)-~!9{=dR8F|Mzp{0?L5n(rcEUE_<+}|mZEUzA{)J4w<#3@+OV7@D=gtSa
zHr4Jve87J4tW#xs{%#Zfkkl+=siY>5m#T3t%*CxPba8~jm()KCE}N=~ZQguGre^({
zJ1+%3LdC=Ea?zZV$pIEw&0f{$fS6
ztmxJjgWQPRQk@+qq~@;UeOq&B?!oOrYF9R^TbouIY5#i6@0n@&$sXcWu7CTpq?U=ibJ{dAaM?Ufty<
zaPmS->!VOTiD;k6(@)jy3Htg=-e&37@Gkbp#|jsS&$T~)?%mD9MdJEt((~$O=`Ia>
z<-_zUNPPy!;k_<#i=u3U)n@HT3$A;VVw8Vrt%<+Ng9is1b@C4~ZF~2yUDYw_-a_VR
zH}TgxLRpt{6%%Hxx%w_!d$Q?9O=qWXo1NB%UFMr^WZS?X9J=ae?e9%KT#F9v5@E8u
z$Rr^WI(uIIJkd9|j(!VXQ?ShaN=RRe#Rk{)`)%}WY}h2c#Zs9VrA=oo_nrFOb60Oe
z@?!VRi-MO&{FHp93V|H;??y7snxo+?oAajKoD4r$FE8JGX}0;zd4|bav0vU@kKevJ{mTJ=`;9E(Deh%u(NmIsom#bG
z#r@P8g>_eS57uW~ur}DB8G1l+W}cG5??(0;AJ5yToA=2+PHBC;f9L;yw+~L!`{v=)
z6nj@7{PU+9ZOi8sSuuV)F|i?alH2C^=kMhYTxEXb-ik=NB{zBp2XL)gzRYLiq^XYy#W;`3Z2`Samt}w}r`;
zZe1oRze%ST$NDl9Rh?d!#^d;|;<@a*q-_D$iXZ=}%=^1=XR-FGJ4@8(C0zgcXtmYp
zc~)1KvP6_ccX0kNP+6kAVVX)pO~j0ISA{|hlG~o_(4Br<`E}&BHA#P2C$Ch}ZrV2A
z+kbn$%#RC-+t&HZPoJ!CVq!$oKRKB@7u6W&x3{?Lvf_xpz4oI%r3E7C*-%xpJcd(T>0lZTjy!|D74rvB|?X@B49RS%|`K!3Nj%St(hb8y{3TGFoq~G`^nNdx$6bkm%#XA}5UZ|2}uB*i7i-
zI+X-N{{tmcUtJ1rY~0(haS8i}SuOfZ<Jl~Me885d!PmgzW372mBixVg
z_O@K<+q+6vZ_T}V$SmOPh6y&$*_D!HUfSdsi55q9D16-Z+5T5Yt@jg-+ZMO?DT*E}
zS`sza^F|xPr+x>purnnMax5a{s6LqJi^(>8_d?w!P?|P??S<{5o(`2h&s+Mi;s*~wS
zTe~D-W`AD#FPqFuOMY?HSx&Qk5v8Q8e4laK!-o&;wyCGh>}I}S$H;igr1{~F%_%Qb
z)b8Ax@j1TewOI7;4~KK4yS7N1?H*HKlzG&*E>+$t-*DhQ0s+r$z0!z`oJ(W3jsiCGheKMYwzt>U%Wwa`GI)6e*HwmT
z-(*ugV>{m*Y-TS$&dj;D`ujSTCkGmtZ(dy;zVrPG)y?xl7G8I$+g1MloX7We=F3`N
z?>n{4VlmBIb7S50bn7+o9xtj^EDEx9jpPdQ4B7SQYY@wtsI6T`j~*@j#*%8M9lkE*
z`uh0wTh=-pknzfH&0Wf~w$wP)yRx$Kjva%q#j)`C+M``vT{qmP*e2eVnh?;Hdc04z
zSjseO$-{K#8|CXG{%_0OSa{*ji4*mQxn&GJxn=XWYviwAwLa~6_4~q|pBKj0uXPD2
zWjP@5;?l~D)925p|NZ^_xx?1!3=&T`UcUSA@bJ#_3J({y%RMqO5ctd!`t;DYs}m1w
z|7SF3a@gv*EIPMm8E$=l^z?-P4b|`8&Pg&5xnin%_SK!4vA4IczjbcY{rKu$Wd`p=
zkuz~j2_HXxd?V#~eMR8ni0yfIBku33eXGV0S28*1-p$SF#q(D_-K0LhX42E3uX_>?
zv%RZ$^wyE-%idKFZtpC9zPYin@jIjU?kDbF9zEl@xaqCa)GVoajR~t%8D|s~{W>oH
z_SM=WQ=Af?9o9V3B_(9g^XRy1mvZnjpNDq#KOQtc+qbk=`lfYAa0lt9ag)
zx2@VTiSy{(4f;`kSh@mPRVJTUv4L+MbAOZi%}+9K)W5L=zP5K?`JDN{hEsR;Yv~7U
z&uVqw-F_}iYrp^cR&j;b6QzpQWXbg#d`{#|Ri6HXQL>t|qIbz_8ChoqhP8sfm7|-=
zJKnN-wI7vZVE%BU{?3j?FgSb7a*dneWqRVmLTAhTAJ1mz?+A;#yYEI@%JM1G*B`!j@1E?BgVT>~vf3l{
z#lTqNlk4uX!{yREXVyt?_dNVjK=t%Px9U9Yr_1JjuJZ^^LhK?mpaoX7(a}-8rjY#d+Gt_2d$kkc0w~)Sy+CYmicgILABFaA1w_9b%(A^
zt5$OJ+5B1&8`*#LfTd{2Y!4}|v#helZN2KAE4mmiMX&oRE+$qa$=$F*moej~!ULhu
z*OHQwbHZ;h+D%)!apT4ppCeh<{J8dh!tylJ4&&z=a+PMAyqvP;qRd0>l-WUF*i-pE
zlnrO4u1_?O+bpBQ9w9KrJy?5-ih{0gZ|#qV?J;dl>o`65cg>z?)y1uoH;pwl(}R-Cxos(v%G8i^o5nhe-=94s8&>5pL_MS
z_o_2p99u%NPI9dHc5B)5d8=pq&dgMHnX1X*AXYq&qs>w6+xz?H8(ZHWd7xmxb=&ji
zr>CdaGPIhXontv!`^ALNPv}uwtjAcDudp#psJ$G6i&}3P1T&
zIAvCtB!7Xwc%HUb!GRT*?sJF~uCq-zz_9LCD`QHuqtNTtw@Y|rtGDe^3vi82^9%t+!pl{(XGm!^TE=yp+6ix~_O
zLC)2igwrJ(c^H`_BMB$390*7-6ye}
zneM;p@meQ&p$kE(_P?_4aMWe;q*i+^EfP*ld}8=<`pyUPW>Re*KQ{c)Nt@`kb+eU_
z(JKx2%_q9uCW!Ma?KyHOPSGJwMIk2b;>D->M~_V5Q@UB{vGPvtzU_A_cj;@odaU<&
z!;~rM_fO2+f9WmN6BF0Y2ne@~^4T`QW6KuSl+Up#pSRpH`n7k{OetPz-RaANnBVa5
zROq~$xbuU5om5BDra3xg)0{RgR5DPup0pz~SN}cdyBTs3*Kg(?`ZDQemg#R39iC0i
zna_%jzFqM8Q+>oCezQrxe$z-rz&{2SA-nRZ);JwYVsgT^DbwJ`H!hjI)WOiXV3P$z?*yb*ZOW{<^DI1R6S2g
z+eG#qSzDJEBJ$f!?|k*DP;HS&EBRf=(n{98n>1I;bAic%N@;m{8%ZzWhp%rJcg4)x
zF;S1{Uc>V@=^x5_y1Tck@PBGi{S#7D72o#Ves#zj`M0;$@_hSvaK$vX>(`vrvL9wX
z==OAwdUpNV_3g5{p(pz9^Kw?!2c|qZn0w{cPJ7qGKQ>uCzP4cs=8kU<#v+
z)L|X2E%B$HTI#c~)*g8NL+jtHKR>d+yx4EzqWDPIg>hBo<6|#Gul)M`tL*Qp+TWU;
zfq|?iru?X1uKj%e!UKPqk5-AZr#U{p_{gwUX1{IwlV|o^t_N=`DhB`EySLDM@wAjs
z!IXKIA1kkR|L?8u^Cj0!SH0&un=^a;f4lpw?f;!z=CrvNB%gh@ymQI~^(jGRA@7(j
zoUXiJ*&Bao`7~iACBOfBV|PbJ)&H-nSrGd1`Mop2s>kCzKRF(|aO*fbV-)Z7Yv%d?
zUfkHQp_5x#d9$GM53gG?!e@R(vw21&ZTqBpXs((mV-&;ezJs#eeVQITJmx+MYyVg&
zDgA!CEmvLh&!1nPiWVQ+X=a*kY4_E!=*_A*3+ER+HISdrF^{8p`}XqZ=FQFSK3yFV
z^A?23-}Z@X?5fgDbNk@%aZ`#Pd*S-2C&aiPM6Ef#EonpRG`b$R%ZqQmv+W#G_WYOMS@Uka
zR_L!aCr%Wzgh{^BVJz5j-0MRVi?rIRsGCfiEYl90UZycMeLo++|9|(47k3;T9pAVA
z_+is<|49C0nF2k>?Tz2p`A5ZzE6rL^#_{1RZ*JbYB@acb7@V)I%sxHs@BHOrUW^}q
z_a8naRx2sDuX0YzzWgSqkH=G%8vS=)`ss|x)K@k>F+@Pn6E_{;hLl-+5NHec3tJ
z*4X!*Z0=h=b%kfN5>v{i0K4V+Md}Rs+HM8kRTTUbUa-zIR#&~hpKn7COAJq>%oC*v
zMMYER-&bbXn%phsfAE9c>leG@ZnWJuYr9@zTd$+q*L(lL*S(tmZ}a}o2?~~0+Ea0s
zRiW;Qpl+eFaZ&z>=Kh<`B70I+Ryrg~O?3TuKT(?X+rfPnYzyzQ9e<*_%4A0#j?O%Nq3K-{*5Rn`i1oZ=S82_`UL+@yT~*-kd4FXZzgt>2J4%ANK#z|9|@b
zComNr@3U})Q;?|9-thHV*IaJ#AOGS0j*o@YVZ*B2+ph%pI<(HsxqRm5p9iaa8YYTl
zXWzHYD$S^iih8o{%$XlHd*e1M8ynZ|K6pW*Y5zw#yTf)ju8Vo7B>ZH2n!I3QOz+O~
z#YV=;WlNUdpF4lvzmz(+V+p^0i%**RrC#r(;-t?itSYW5swzF+ob~G)-Z$447wVjA
zEPgo0!hGLhp#^icR{WbT?bsZ@FTsrP<{Y&FYNG!14)_uOu9;?le6C)Vn1sM3G$anb2Ej^Ccg
zJ)aBSGv{B!6vMra^LktMl=#h0p8b*fI$>)gQ<)Uc_lZsWlwIGNGfQ}$6!OTscz=zO
z3)79VpXIZnGRJPYn9x>rkw{Uc^9@P0Zavm9Z;+
zwoJg!9qf0yU-lna|B;L7TwhO*n8^oW&vmmRW-0x9oqkW}k5so$jO@EqrFUofRm7Rx
z?HVd)&g8$Kpl`m>OsGB4PHR%?r0AtjpFI1Qm?`r|-@2l2j%@S_~)ZOt>#Kvq1J`{*0V?`&Uj`cZzjg?&>>h?|!_a
zvDMli!?KYNb`n>GaFm-1e`aR8P0j>f+Pw05
z&DEQ;J@iLx80(HLxon4*v1)35)fU}3t&Z2maL4TCw|U1H*cVrylo~bb;BrTmnHT1qITNyL<$lY^Z_USj6;54yeO~YC%S*MF
z=6bF;@7P>b^{ddn!iIT~Zc68~AG53{A3CwMsy234=iIsv6?`ro=ggKZeJi&<$n}Ry
zhra5jWy&Gu5@x?F``eXG?7qD&-KCp<88MGUhAF2Z7=i7y=M$uckn>i>ypnR
zGB$CM-yS^Mc&e=VVX)@;$KeaEH>}9eZtOVpTVLef4K3}BHC)%_&8DgU@pM}6W#*(l
z`)k!UkHwXDRx25Eo|Qi8@^Gj1Rz{ump2CHC<_TAGvI&L>x%
zdi%ktEw^}=Y|fHo>on-;AOcU^EH>_-fn++q>&&Bd>;Z_drVRq;3d67(z&w<-tzFf}Xj%4)zys$JgGiR0CWicz6H+cdl
z&%}iqbRN{Ui|u6kGWr^e&X%f>o0HIZ^Nl{a_Z7$
zd8)G43U+HxjuDSN@_Opg(x>-}`K@genRfsDe!ppVp=p7N<>a|%ul@UdKgV#95`Pni
z)oqt`IqTd$&x?+gJf9L4x$yGQr=8hP4dU|}?pOZ*`{qZ7;G08TqHBH}>*ZqZi{a|j
z&i(sBnbBq5CYMWDN|9D~Th|Ksk1<&{Uu7PnZvo|%5*
z%$1OBC!7@~aeS4UyY7kj(oK&zP0b5Ki!Wb({$tnIn~o2^yo|3GJyht=@Z!iQVmYA`RCF6fP7MO=?wnz1i)Fo!wj$7t2GzbJWX!6m%aj-0JeKBwa{6
z%Jsgrn%%y+r`JvpsmwK&IQDDPS4ZF1XQpVrWA!UE++Frb#d=SM;Oy&rcE&PHvY2SC
zE6DYrKG4O?du>^+i`vs#u_a4PK7HN8e@a**YnQMC)BTs1+t2QOt+)H&W6pB5r>)bn
zw~DhLJ+h)@pJH4=sq3|!ucmF;!f1DY6(hGv=A_#Pf=eE^9jNprawoUA{>@T-h
zI7dm%UJ}IHcJ8>vZQ~=al_$K~$>zMxf49C~`@NdYPTrzHPyb$OWX`#rK7Z=zPcw?s
z-)U4Iuz1p?SS$C^rXzj%YKybA8YTIeF}zogYriW<4&R=?eumJ|t=Vs$>?+Oq-65D$
z`TShYG~sE-WjeHO33;Yib?bpL*2zeC5U=e3wCx2e6z-}Y?VCJmnNVjp%ae#5w`DQ@9Ot*hk+8V)l|a*b!a&C^yo
zRnp-_^}Y=?m6{4NPey2}1=WzD=jWahQ=5*%eW
zdOb_m&y335w85=Vb>8!-?sM4Qu4XuYRy#c6pZR@`)^n-+woeN8Jzlw~U8Gm{de(ZL
zFfHSjGh38BT{V9x+5d>Bd--{**4sJj=FYX+csO|Zru}w58qQ{3IwD?+=g$h;x|_IO5Z$padB?*+4j4Ua{SNLSB5-pVBK9QP!p?vF@`nIRN~(X9|o&0
z*P_>acoSLn;`#iWX2!;@jEh*s99TBIk+KdeD0cFGb+kL@Znu8bRO{lxo6Zj$jjz7?
z`oMAT5wm`+vd{AwFJAn5a*FK9)h<8fP0l(0R5-r+-R}O>**DXY2ewl&A-
z*R=5OZ%_B>M5|rd_C)Yj=NcU|ZQZl09iFV()E*KOyX4}W7jklPiv{M}#a@m39-2EX
z?6$>x{h$y@j*@?Szi)c|`#X1+y_bX2wF*Ptvc8jbzvtIZi#>BTHM~zQ_4@faf;xU1
zTb~(E(fe2NL~y0b;Nu?K$V|TltM$Eo-)h=AZ8D0OUt9ZY
z!|W&f7W_EN%r3<)t#ImDbMMdh(>HIH)ykKN+_dD7w}hN#b%)C@v5qe%T3Yrw*`1J6
zI?E`s!EbrBJHv#y_rKqDD=k|rU$1lKz>dnVTC?lV+orNrbMjsaD_FwF+|nu_!+7EB
z>FG!RJv%i`ovGnOZ?1~Ct)5|_hC!lu!wrxVZ742aFLg(;EWCY4k@b+?0R{i(vDYQlT~#G%lw%Z
z7k4-~J^cDQ=ko3R*|Hfv=53~${0&?uOxLNsdvf;Vv7)F&k7TU!qKb@Wz0EGliKyQy
zTE5D2PeSoL{aprYo?c!jYi*V@!(&z|i-d2Sw{^)5+x^aIRnPhDr|iGj(`Ri`zb|-h
z`@I+ESnggn+`5EEy_mzTHR-Ay}KQ1C9M
z+biz>r_+b#d@b6({@xzVmcLV1zG1w}%>4PvtG|IJS`TMljte-<(+xI%sPW5)bR$LcPcw3q-)G#e;>+|_tJwC!464@J=_62B}vMkWvH_7GB?bXwL`!?mD
z5;2?o&Ges8^!Bvb_o`lRn_%}k{(n(c;!5VtX_cX$X9lP8&+~~{b~UbcW8z`8GUkkw
zq(yf36Er@EG9;RYtp2lPLBraY*VkW(6}-qM@1*u~|75j|k(&f$6mDB5GHI*%1m$)7w(X9QK*n%t%t8}m7>)y
z)(sa=N9lPmUAXY$hfh4?1-+nsYcs8?kM-*tXS9p!=@|daul@gTJMWu?m7h=9?`@A+
zu-!eSY^tr->%`VF#*W#`5BMi1@vzK$y^lfR!~8>^pX;t?DSIgt?JPKJW6sA1%}uW-
zz297aYs+mm)9YUIx7adWf3fyv=I+kY%=Dy#n|Y-Uw6fZr7k|2iOQGvf5U0iTT175v
zlY#}IuJcr1Z&{G)ZJ6l6yyf}x`DgWZ8=Y_QS+8dF*2km%=?%kei=xfvGfs#sKepIF
z>0)nszo;-s
z|MR)``!+|cv)Gd0I8*Hx>)NP|#`S-nPLXzY*|sNGeLmBKJ99l5W-Y%sCqqzb6`R?u
zsP0253=^;1UUmKbr*A=QtKOLV&D*l#!2aT*8++2u9@!_abbC6#+?HKgt7^_VeVEQ%
zv#rgz+e=aPfp?5YUgxIEms*03t(h|AZ%bzzQ|C5A>&2^rVx}tAbjR(#c$=|BF5UaD
zwMOidUMbaWb1o$$EbUQ}-Z?kdxqX`CkvabMrBw%6b@W)XpJg)$IZT-qrF3Spzu!H#
z866L9Y`AqTYU$b?!FRt}-HlnF_gta8IO~i=w;t!(71{IZ_vLK8+qTJnUHtiHcWcWv
z(-#HAWGyOptLgReHgaC9InVv}#?H=+=N$Rj{0&y_?$?*(MucU(%E)IEn3lP7PvPR!
zf6hlD?(OQTlii`Yy4LpjdFktUW^(TT7cbm*;h>-E&YYUZy!V;;w?*ZC6zOf_?dm_i
zsXS3eIxIac9(R8tbCu1Cc)L
z#d?vd46lEz3S~MW!y54Kdi-y-h7+xSSA}ghPfl`U5bk4G@vY^_ljD0UO~Y(noSpW_
zM|rjE{km-HdgsJ_OQ-QnalWv#!*z*FsKV+I8E--gXI`RKR
z_>~P7$&W6nE;P{M<#PJ?z7#!t$2CtyT|8`
z%V`@M+-zpQJ=K*h{?^~i{%`u_Y)@^ejws4~9bb7gDcH{FmUi{GFFQGMW`&)$3(_*&
zx-)T$Q($o7b1vD?+iosb7Ulk38ud2(oR*`;BbR<-$k1kVvom05QGUwd5-(o%
zUQ8_bz#n~0!%V+#e?E2J+`?3~cB4k~de;QuP6z&;URi#f0-qaSIa*fvx@;2pR~!^1
zpRaPs-R{?kcakD6il?35cjECK&RXm6buo$0H%yo^wHp|9(_`Z6CtJ2e-xq3_mv!SqXhLK1Aw7oB
zmm1TIUw!H9IB~{Q;#_64@0&Y+ewIZGs<1Eh$ca4OcdY1GSyABn{r{}0zg%>`soZb#
zXv3Emx2BdV36y4CJ*6@C<^Oqich9z87OsEoYa{!qH}BV8+L?NK(zz|Ux0jqedZUdu
znvvVfnq!NggM#-%k!=_6_nN)@x_WP?N3%nG`GRNxmhc(7{q}1WNFCG-Z^UO2BLCuanTPS6dSLSQc@P|
zy!u~7Z=Lb79aY|TFFv%(Z%VQ*(sx=c!N|5*U!ZUCM4v2y&&(&Z8uAivuZi6J=*N#A
z_YOF;FtglxaV$iMpG!F~$YTFp=`{k=;&*OI?Y_HKWOv=4K!)=|T)&^Ha@aSl*{vV3
zu&1ZTcFFIQzD9@E=I&l@{)mg$R|bpQWUO;DzVw*k)QJT3fUXV3znBs}{9ts6+kL!O
zd-n8=3=8W1{@Qp^WZmi17Z(=3dAt37+Fa}MZI(Z5tG*<}*M7a4`+S2+?XNF}a=$;F
z*5`iTD`D7F`ts7zSKt49p8wxu-s79;^Ve#8|7u(kA04xH->yY6tFL<7tTK#-_rcc~ilg21e$VD^83%(`(p0)~LGHAA5dq?sEB)#pi9)ZQ57g3BSC|
zmwWNz#qzE1P9LuCvEf+OEfB(=r=x9pHJLXpBJyg~>s347Ihhw^{2WVV4G9KG<20V?4+q)TGG*9aVlP=^+W2)xx6%ZR-tB&G#=U6UlVj6A8~Me>$yGmSWN#_n!Q`^R?#H<=UrN>-o47=v
zRDOlVbK{W2O<6Bogs;6&pZe-<%)YFtyAOYxoFP-3(8iJ#nZa||X=X*hsobTk(hOFW
zpHyC7St*=-X-TI-z_Z8w_UFPDRCrC}Fv_~3v6`Xk>(%hy?^lZgN?3Pu_+@
zJ3-9lMD*L|*|vT&jh3FVoWm%1M?Ba2-OlH7(C2iT2@iN@
zwY8+=TzKiX%X($Sg8KTfCl28XYgnT69_$NS@GDR4Nx6u_q=fnR>;K2D*)ZSk>_6Z8
zhuQCGGI%mbm}E?NaA;lp{&VxHUTHq8W$@%%RDQp9dhG5p-yQyUnqO9);#P`~&yhCI
zt7)t7H+~@_TG2YWf3L`$8LPr{*YFD48(aQ-9#eR#Jvu9S_X*#alP3$`NG|>Vcl}0P
z(a(1-dOg%)aQyJ^;o){xRwkdbv(3$$PDnC%K5tvCqm<3f%^u(>7QiHUXLicccl-a>
zZFVRTaryJByD%mz>(%=UrE8%b-e%;RGW-AM`M1wK*V!cJS;+hX~j
z>cU?33Nbku-uu}nXIU~Sb=q9s*vczy_J%KZaZTX87q89C+f0R}TUuN$nHA1+GhTma
ztw8G=_P8p~h|sMz{JW<5pLdKuUpe_2e>wxpPyR-sJhpW#b6DOx&is4wt`+wJMdvmS
zF^*{~vahd8wR?Sc_x2BS1zfVWolE8KP
?`(KYY4@0*(sBquyKKV=Gv&>_z
zPdQu)3LDe@&N4OJ>0_#_np)<^8mZ_KTOqR4_;SXEDeLd_F62|2ANo+IVP3@}&a#Jx
zSj#>?^ZjKw}lO&UDYN^iym9eeK+g$T7jMG4vVCUL}l`CUCmbZ
zB4DQGQH9Iysk$59F5Mq2ULA4j`Sf76}pZN0NBw{>DhI1@+LyF4FLmWQ61*wS!FF{e88E
zDg)WI-&d-7v#f4jWBg84Q}bo;i%pBx`b+Ay2-zR4*tq5D86VHc!?BTpI~VH(g)x2<
z3vYDy(0F&V__nb>;|gW(=~k=?SLQ7VI@H98Dqc3_H9RBnmjvFea$JIE3o`w
z%=EmCcH0GC{d#mN)Ki4nZ2tO-GPhr4G;NGD-u7~$d&@uBbk)`V@*fM?%&w`<@p5?Y
z$zZ^GLE~mk^`A%FmKz>!T5y0@+Kl6F=d?Q=%AGD8zk1hb%7*2$*MDKEsy`)N>-k_|
zNB7FBuY^-dqdwQo&nOp2k8au3_vF+sclZC`a_YhtVfso20j!%XZ0zce%wM8nlYMofj;X_zGrkI&SQI)o
zt6aU^a-g_-+xC)@^B+Hcyzk`RxMNE8}*rgGZ&dJI7d7*Ucip6a6N@AzQL}vJ2T%M%WQCAam
zY1P&xD{R;uw_N3YcSfq=iNY$L2ZjQ36)x@RE17hz^m0eJRMnZ%nVX$LE4gfRd=_@J
z_p6?qu_7qfb(s~vryKjcDeT@`_Aoh`%w4j5M(^ORg-w5DqHmt1UltrBN;
ze)THz2>Id~jXiz=0ZN;$d=!}(Sn>aU?^BkGlekLK^;*?
zIF@hQ!lZjK|H-LiLiQWfl0DomoBn93@&E38dy)RusBd0QhaMi8FoAtR^R33llY0W9
zE=O1NUw!B5cR^{%2_6Nd@@?B~=A4*&ai)Iv7L6-c-+G$I%^2maL7Gap`OO;eAJ7?oXYHEw`c>&hMBavPNUu-o$_aZN7Oa
za)nom(pMgus=#!+e~rAv45#SWUMW+lem|c-#jNLrIky^HS5CYyG;_NZ%RC{SAl_iHnsm2>Q!*Y2Ab%C#g(*?m&u$?JH{OxkCFgr*w*C&|owJ=h?B-hVof4R@t`=9VY*yyEdROWECkG4@cr31W
zK1>!qb$r#WxqLF&*H-X-e_v=4HOs)zb6THcZut7R@BJsY`Ub4*Q=GlmQ|0!dDM}m%
zSPCjpX1m_~Mh+LSQ$pOV4TVuM`e(_2E8H)roK
zJv_PL;lwE{voi18s`0$`?Cy>oy8bS1bza{u1#PlWR62jL=z+^eLyn!3cluSX4J^%D
zmHp98!$&^okkZMRXg1Hue5J4L{?+-MnIS)E(zf#+o|+C{*iKIg_FBWL`sg};Yms-t
z69v5o25uWXYTTI4_~d1>Tu@OJtr3vl|MzDb?{Sm$vA;QEtj|B^Sl1OTqq_fkLEZbt
zO&{*?%ZW31Zt$3+{L2Y*=woqJQqugGSxWoDGgEA68H8;tkdE-!Ck_eO-RYmG5g0
zADXu;JLlAT)44T)R__{%WllcwIKcUd`S@1{u1C$$)4%U*h)}yEz4DK^PNb^Fij7(-
zPYy0uR*tW=*UNjuzH#H~HjSyj_j`ElVmYd`zo~=$KvOj1jlK3e8BFc9KJ5QuUXG3-4OFyX?7St@Y~jzh0@ozubJzEc%|5
zY+ffKzkl+HH;$Vmj<>0+%~R|vdbj$>5$?qvZkO&Wzu24jMe=QNgTp4_<9j@hKa%lx
zbLBm3S@`bN)z#+D?@v9sKYEjiP{yPmTg{$ak88j0-5_uAjy1?sZlXun%03>S6}+k!
z{3Bzv6%>3KLNxP@SVNQZ6lXd8{My`fYvR=x6JmCMvo~G1aIWGH>Guzf`6ldssF!+N
z`rx6?4GF0)7F}XGdQ*V&Wg4Cf*6*t<$f2P{H7_EXzO7_=lW&SBBw
znreJ#N9&5ftc_2mdFw5m|M733&N;
zv=$ag9qi0f&J%pJbJIcBNegNsco-VJwZwJV+h;MWbjnXYa9;5AdC8;umFLu_YwTyU
z&!`dcxw_+0RzZ=}nPRQ27WI=FLi=}51xbke^2!S8F68JC*br*r)8bNDy+
zs_&i2@+NkC8kQ?m5?Bjb*$bbt7q3qATXMenhascUf)fWHv7eD;(h4pJHD#O?bLMSw
f)jaEWU;mrg{E>`n-1murfq}u()z4*}Q$iB}?j2Ab
diff --git a/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 53d6f5bbdf0b974b55cca892794e00aafdd39502..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 13447
zcmeAS@N?(olHy`uVBq!ia0y~yU^oE69Bd2>3_*8t*clj1{5)M8Ln>~)jb*P04wcsP
z@b-S0mzjCl%=eOstmmyJ9!6JJLD7N+9`QWZzejCV|Kq1;dx-}&WKkqwzrU*F
z%Kz2X-=z)2w&&kJFzw*|_TmRImt?2%ZD+G|*u5~)xSlUcJjQzm-@1dGN)w%}UY%JK
z&0N&5HDGn`jWw4~ZhP`<)rMM`AFBQkci1xR@f?fo1-wwv;7YKa0o2>rts@CzZKU@i*pP~+OHHQi
z8s6ONnRhcaH%;A9^x<{EA_@EEN~bwT$`dZF89$-O<7N|
ziyoR!*rud;zlfQiFGGqP=pf8zROWH?ls{Xi*w)2^Jt%bO6KGH2OdGEZ_Lkn
zu{6ygK5vR_9!Ets=fVBXqDB|q_q^|`PO#GU_;jK>m2b_Ru(|KH=~n%Xc;+!{>!*2a
z71|tu=blbFS#H-?-B_(Ok5Q%aiRwj}joZZMMDDGdxbpFQ`{^EC43Xin}CTz2aFvW8QO_f6qQoMiIJ;Q6%w@}5d;
zf7YsTsvrFHQ|ra*uac|IaYYyMALTSZ8Ce!Q_q;q;UC{A$=B1y^JPvH1&oR~L>X*2@
zi!ZA>-*0>(zhmK5cU#`tgwH+=@oJ3s^OUB{Jj3z1pnvJj1H62OAIhIRIkNf9bJL55
zn~qLD@P@}oB8{ake*MF^$Ma@ai(Lvl9k$yS*mT
zS2kUH{{P;z$&-ELkIa9xqEElo=(R!
zIOcV#bjGMu?g({^nRV|?zRJ$W^EP$#MIC#e)IdZKyeT-~&LH@anhxK6rSe)|(wm{y%({Q7#oNiKX%@+-gVR{iBT`KtO}
zPQ&w%1=sukoId}at&^p!p*}P%O=YXrWuL5al?j!{G#;y*xc+k4`H!Y4+B$FM4mQzpQ=VA+#~w!|YYbZ}GI0
zEfXfCU2U;6e_gWu^6b*4#{DjnY~mh^Pcaah6ESV(%|NcnT?fu*pV9oW`@-xJPp19X
zCqKA!Y0Vn;iV26)Jry?n`O5X>*Y8MY4b4lBnFW=!io3;AQ*ECdH+V2vC1KWnxvhWZ
zs($z_$9XMSNaw_vnhWQb?6|o6KzzcB{>nh6{rQs;=7v>g_8nCJAS+bxA^*b5qL;oF
zYK`~HJsak)-g@h?@ukbrKUp|^9{s6SX%8>zS;?Zdddsca$yc&^!=H0jtSH_YCLOrq
z^4jSK^mz?Ar=_Ok{E1xJ_3;#wRiJfm)faF7X(|0CN0=i{ELVw`GSl*I|5d(9Uv9C$
z4sOBu{w{C#F6CKoazyg+r>{}EGuL0Nd8q!%?xC;XeDx!>=1bZ2d5xc5Nhu8zn>lsf
zz3bX@lKB3W&YWo;bYcGEqq&!u%O9RNXU22>3;U@D{A*5}vH3qe>h870o(E)S?hjoR
z@ayO&)w}PV9++gim3@8Z+t(xI>i6fHwB^6k`cDola7+pBmy>g6YjHOZ64E(y=8jE}
zTKAXFN7Q2099uq5p|)gKzRHJa{wHteT1|}ITQ$+v-`4fp*SFr^zP@(Bp(0i|eUa
z?=JtYq13>Vz0%$wda>l|;~Sm?{$sdZvoOvv?3(kWck3S}K7Tgpt*rGV=VtcFn=31y
zoLU$A4ZESq<%9Sg2PLHm1-8H)F`Rht_bFN+KuI4+xyc5iY|7Blx+wd&>
z|KIIV4A#kWB6dz%^UXkWOQ+YuJzu^YwqJeq#*xMM6w1G|yIcTdI-$KLzvUte4D=9K7mHv3~smEBL=
zm$jPUIfY>c=bc!-!uATosd1ox}8dKyz*=Pq&4<`|LI0enDqJi
z-FXbpzr9i1e0#e%#{tg=?~EDK7(;r_UCA)Bk(8Bvr2a}PNS*z1;#c!mU;VwV#rv0)
zy!GDvz;EsU
z8E4KM`*Jw<%S*5GZ*mm()vsJ};_bnBr>8d;vT~p330tex7;huIW3!BtOUB-V;x|4y
z1qHPP=!A<0D(3yYo}T)1&)55P=WU;yy0MA>7XPFB8rse1p!Q`u}?r6XIe@&2s(8-7VD-mBq$eYrQ2NT0+*
zffu%pe2mrF8avfjT>a0i7WHUUS(jeZ+2^6|;MsPkNRclXJjYis9v
zG4PaY&t~{`!9OD{>ej>W!a`RLJlnjkhr^KRj>FdLo86vIw^Z3@Rc8Nh$L*B5z)v+L
zKZ`bPh&Er|d|&pd0Dq_6qWLe?mww_+xSV>#TzP;bqcaW7GaV&osGl`1PLk*88V=DOi=OUH6+)
z@aomtZg&1j+RObWPV%14a=Ndvs!k`@W`DlC<;Fy2ONY+z4
zzU1N9Mb55kLaOqrejS}y^i%i13hxwwVgso)m$U9q+hr3H`))fa>EPRkW1k3Y*fC|&|9Y*f
z1qoMPp8KGnRv&db#gVn@?<1wdtxNgxmw6g;-6-xBySZ14;acta_xmT$%)NC*b3?A=
zy8tuB^chlVMxJZq7I*dVh&nAgH^njL-a}>&M-tIZe-5o&`vn*eE_UTYg$$+MlcqQLAgGo}AAW-~TUSv%tSI
zcXoyeJ~;I6Z%W$jZF1~2<%K0C*^f(j8oD&sg|8O>lrbSVMPR!90u3q%-BV`F#$_Iskpzb(0ie@?!<%$pc!)we!cy(IC2+kxHQH(54x>J=qVJ(b@OnQ(m}S76)^
zH}`~ne$&-k?Q?4sLfCTue+dpU$r71a{CuvN>Z*W)oQF6#^*s5m+Po-LXue>T>Xh|L
ztLx@5r_7dv4c1HkOaECBvP$J|{{0Ad`7=7w##<)8bUq_*GeahFTTjmakM2fqk4dXq
zrYx0jSo0y|75ko96Be+S$$nzKG5hcH3=`R#$I4u|>f`!jVO;mN
ze9XsS`Z<}1`PSQ$-6xg*|C@3$xqqUl{vHRj`frjuYu?N_8m9L8|L^$z@}Af@vx9#c
zre3N46}j}#Jf_A28)APld|SqrdGVs+6~;I{!3Cdg+*~`m&|hzg^7`7(z7G}8FPK)d
zx3#@z@4j-S>y-A@>1M{>HATC%G*=;$TP&TV7S=-sg;wcB~>qSe2?PyfUcW_2id
zh4P=2ni?sCKRUACtPU^=o6eg3!c*UibHk~}CP{yIX8b=hC-SM{`6Vg_rxdlS`2si}
zXIze1!#LyR!+i~_Ws~O#%wBmhMoMFHDO20q!j{*(lTI_gJl|IN@}=#Pr~PWb_SaS`
z{!)JN`BBoQUsuD=+C7<>qjn&zLj2mA!c(W#Z_VGUI@LEqkkiTSNAb=-mI*&ZXX=~?
zbDtrsP~e&NO#4z?yxQJ|HeTUx51w*gcplJx?UHkw%&m`aHv8Sye_)|{>B^m$l{t%k
zc?7U3IW7(k3Xqh_5NEVn8NwSFy4}dQxFzI&{*CkH{`2h)Ju7>}yYlo32ycUSxFFDKPm)o-q>{GaeID;-
zG!;D8-#mg|A}kojQR18w=KKALU_*_wx7x`qJ2)5h-qwgQK{Z;
zv-EoW&GqxIuTw8@;d5M@Pg>Bkzi9^4x2|iF2WBRM4xl+>hVr0C+fsga+w`Crms#^OiWa}5*D31N}d(Ue4
zIC}n%x5%1u`8)rybMNno2^L*3WDA?fJ8$8|IrWBm>ufjVh%@e+d^-A)aIDrBMy8lM
zTTU)BSd#f!E939q%gZ?H?Kju%4!EBG%i!S_xmou2cUs#8IqB9u;#?mWm$^Fm45xwj
z;+>zWa&p*ba~_Dh^P+m`C$(3B;?>rSpw_ddW%;?R3%NgEt=1B&`>5Q{vDc=uF5&F!
z^|Nbde6}tZTje;{v#I{`C!4CPGd9a6Cy1_kn&12Em9+2dd#pO;+ZCl_`j_@e|D7=D
zQeV^lBeyv>e1Exo?e+husZPdGmsNW*GBw_D|2qHWrP%dfdB4Bi*6NOv$y!$ZO()WA
zfAe%Egp_<8fk54USV3+&VnTxVm7`JBa+lFofnFMIZ_MM{^q
z=*geW^7hfPz4-b0``h{EzZIt2e(t%zcx$EL!qC+$N?v!>v;-GC`Fe_FLsqOqty*K#
zaSf4N%^&M4j2T8%rCh5pkCw&J?kLl
z7QRdSE)*?QvrJKGa0~vE6?5nA0i($d*905(T2zD-6}>(i_l_n1^@YZmn|p7+dUv+F
z>dT6pzwh@>j}g%A73tqw|2OrTmU3Br+Uv08&sjIX`1m+rDHr_cjlw
z>6!7F`%*Qnj&K_2>b|{mdE>_IH+>qu`7rIBK4HOf7K>MBD;u2UYZA78?#^{T%Fpoj
zjJ}LTz}w$CuFYnA($6NnowOmlM}1pF`7)VDof}qWW~$3~cV=?yY~SX}RLHbPMt=Ub
zz~#5!pJmxlR=-~Rcz^h-r=1z+p84CYEc|A%C4q5fmfJLk_1Cuu+?y9z^J~lX=d4*;
zvmY!K5Wf1V{(W~}UYaqZ)z6=bf?_r06%|GM!ggPc=4|-!|7q^+HB4ec5sxCQuRWW0
zd+X|B-xprx{Pj1au*JGm=o@3P>&h<&cNXv6W^-uPCQg9{sXQY))6(+Q$9gX}KK)i#
z*BB<;px?GoIc=}s_G{6c8&;RU*XoYZ>E&f+58L(d^}V{=Sq_Vy`%hg{Fx$+@aJF|@
zua2IWP~JWlhKl
z9}kIJY*YXIc`ciYm$FYW`-Fh%e}68gMlXN&^Krk_b*+l*c?r5lwPro}wrT3Y*;j
zZ_$hw1W>x&CJuUcP+#!!l^$j)MA-knn^VKN-vf7u01R4x5}9
zyJ+!5p4-oTCn{{wve}-}>6@_X(!U8&xr%~m&PJ~+0e(CQ2&v7Hw?auu$rjBC9i}RN+DKT8f^O?#0
zM^4_7ZpG1SkP
zQ?sJflyjk<(JNB~da7^o9Xsz@znV`s)8|j@xBqwJp<&I7
zfKO@X=Pl7^{lKjo9VYpI=ijnV7v1GUtNH%ywg1X|X7Byk$3E*ug?!1qx#`iyIWm_V
zSN@Tk!E2hZec@Wg3tkfyaw8&G_Po5v<(kj+FUbEJ^M%*{MY7{k-^;~5;t()fd-zfL
z!J^=x+8+5zZjVtfr)Q-2CU`
z)?cmKA|K$Qd-SQS^&7PVimzCS?3F{~1z9R}~_o8K!~vu|E$MMH3>TgX(g{p%x@1sA;I2srqJKXCQcj{nEa^X^D+Co;V)
zZ19zR$WU2fq2yTjV$1CHm*;+Pkqo)9$I{X;bJAUw6Y@4UcAP8YRlQ`o?5`@*mA?xv
zEtlEskeByv=8YRS^bTarI9SfR;X32GC+tTgR=qr+@$=A`Ge74^@ZG(>US4x!@n)+p
z-yd>63Ot*cC)n-MuxqmYm-NWlX1NOWVSznbl3~);i~ei>WX#Z)O`j1YR3E(hzUygD
zf3Ehty?@JAhu6>aZdf=|xBt(9U&;qlxEb?yzcu@G$yjU^{{-b<_pk6j_}TDd`AVLs
z@oS!(dVTF|#A^Qd13AoVvN$d*^%gI>;iP}TJH%`565})X_Evkw#mPyUVpG0p?JL|DJY;Di;b*(SXC#lspTr_Zh^nY4Pz~U(n9zE-K($a}@*QAL{W^*zm_|CPO$|GwPBC*)5S4bzn
zuFlTVxmF~v@%(&y|6_f!-tTri=3C8CF!j_X`T9Q-Yrb9$591f$acHe+IQs6wLgt_0
zSKdB)y?(#i+1cjnC$hZX^SN&V>+|{2M-ElS`ZCS?!_+y|>uL3Yw1R?yIg20qcRvi6
zIe)%F^DOhMDVvqFv)gx@pSA7WXU(T$a{OQPic2C=i5J^*cCk0uvN#<3aku=wDN{@T
zwvf25i~D|MUCf(Q@wP)yStqN|kWnvalT-QZdDm|-3Nfs>yxiY>Uh&gYQ(4ZXmZ}^u
zRri^ZprZGV*>3%hI|_}f*Vw3zyp+@}p&Kb5|2w3gobP4S(Kq3UTW`U{Df>fK
zUCa?Hum?4r4gVV%pIfDS_v(Z82^@(q9Z})D#{o>-{*6Xw8
z?M^*C&2KK}jh&|o?`_$#<$l9SH@2KMi{O7D6CF49N;@a3+KCGnU!S#RP4PR8W`$ZA
z%}Rk?J9FL
z|4++Pb9nMX%h+Us;|FP-xwmH)aa@~!^q>Bbqen}b)tY&Q?ymf_+{;Ac&UyVk6BAbj
z`g(iM-gWZAge@hTn{VH?Hhxu{bxNfD^dxS(e>=SH)|bwen&Zj9b3*Y&W@FD4^NM#n
zpRbBY2p3=o%Gj1M_u!qK#oB-GPGR|=`Xl@M+uQ1U_V4;^?aXH=H1$zTWMm|FgR5Ho
zp}CTO4QGouHz^)_ka2=tMa5>9+iKn7<&h5_EO1!;=H)%lVM<{5^5@U%Yin08IvVTB@I=vX!`c`@G1sKiUrc|<@Z0~{V3@@c&$@Zq
z*6SO})gH`@zr&pM_j({tmW0^iIaB|v+Gxhn*QTSvk-J>fSy*`f^34u&+g-X>xLWx-
zzFspm^2X`wCGTTPA{@#)ZYeQrT6OGO&vO6pU(yT=f8TO39oTh#w)y!P(|Np6hGv6;D@hV)?XmdX8S}hUpCbcE2)~vdoN#XJ#m{
z=;eN}a;ckG<$(r9Q$B&^%d+b~9AtNUE@sI!&sc!}2m7C2^?yFL-+a+;?0I0VJ%`uY
z|7u-(dD;$2gj?0@o0
zuv@uU*E7bk;rF}U>o1x5Jbt~m>g%T0yAPgR`|{TJ%hgr0*3Lcl=F;Bk?^>IW7;f9i
zaiUXa(_UjfrhmeWq_KzWOK4jGu0~f7+af500@td&s=R-_tvKOXGQ_
ztrCJ-OTsQ*Gi$%rce%Co(4vydkqo@6e7u!n92xfb*YJv*wrplNVH{ChyRrHA%jNUK
zensEkSKFH^%+4>j=F`nNw!$+q{$D-wOWp9b{2Q<5*OSsD5|?{4{|nkXYr=;cn|gY(
zXJ=m&*En-lw5+n^Xj-w2