From 883024018201db26a5cba85b678c15511035f271 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?= <m.pich@outlook.com>
Date: Sun, 1 Nov 2020 16:53:31 +0100
Subject: [PATCH 01/34] Add conferences (#1004)

---
 .../28.json                                   | 1842 +++++++++++++++++
 .../github/wulkanowy/data/RepositoryModule.kt |    4 +
 .../github/wulkanowy/data/db/AppDatabase.kt   |   11 +-
 .../wulkanowy/data/db/dao/ConferenceDao.kt    |   15 +
 .../wulkanowy/data/db/entities/Conference.kt  |   35 +
 .../data/db/migrations/Migration28.kt         |   23 +
 .../conference/ConferenceLocal.kt             |   25 +
 .../conference/ConferenceRemote.kt            |   31 +
 .../conference/ConferenceRepository.kt        |   25 +
 .../modules/conference/ConferenceAdapter.kt   |   36 +
 .../modules/conference/ConferenceFragment.kt  |  102 +
 .../modules/conference/ConferencePresenter.kt |   96 +
 .../ui/modules/conference/ConferenceView.kt   |   29 +
 .../wulkanowy/ui/modules/more/MoreFragment.kt |    8 +
 .../ui/modules/more/MorePresenter.kt          |    2 +
 .../wulkanowy/ui/modules/more/MoreView.kt     |    4 +
 .../main/res/drawable/ic_more_conferences.xml |    9 +
 .../main/res/layout/fragment_conference.xml   |  104 +
 app/src/main/res/layout/item_conference.xml   |   87 +
 app/src/main/res/values/strings.xml           |    5 +
 20 files changed, 2491 insertions(+), 2 deletions(-)
 create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json
 create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt
 create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt
 create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt
 create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceLocal.kt
 create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceRemote.kt
 create mode 100644 app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceRepository.kt
 create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt
 create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt
 create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt
 create mode 100644 app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt
 create mode 100644 app/src/main/res/drawable/ic_more_conferences.xml
 create mode 100644 app/src/main/res/layout/fragment_conference.xml
 create mode 100644 app/src/main/res/layout/item_conference.xml

diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json
new file mode 100644
index 00000000..c7c4c033
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json
@@ -0,0 +1,1842 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 28,
+    "identityHash": "3a449a55ea73fbfbb7973f1f3f834e10",
+    "entities": [
+      {
+        "tableName": "Students",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "scrapperBaseUrl",
+            "columnName": "scrapper_base_url",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "mobileBaseUrl",
+            "columnName": "mobile_base_url",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "loginType",
+            "columnName": "login_type",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "loginMode",
+            "columnName": "login_mode",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "certificateKey",
+            "columnName": "certificate_key",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "privateKey",
+            "columnName": "private_key",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isParent",
+            "columnName": "is_parent",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "email",
+            "columnName": "email",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "password",
+            "columnName": "password",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "symbol",
+            "columnName": "symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "userLoginId",
+            "columnName": "user_login_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "userName",
+            "columnName": "user_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentName",
+            "columnName": "student_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "schoolSymbol",
+            "columnName": "school_id",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "schoolShortName",
+            "columnName": "school_short",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "schoolName",
+            "columnName": "school_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "className",
+            "columnName": "class_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "classId",
+            "columnName": "class_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isCurrent",
+            "columnName": "is_current",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "registrationDate",
+            "columnName": "registration_date",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [
+          {
+            "name": "index_Students_email_symbol_student_id_school_id_class_id",
+            "unique": true,
+            "columnNames": [
+              "email",
+              "symbol",
+              "student_id",
+              "school_id",
+              "class_id"
+            ],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)"
+          }
+        ],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Semesters",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "current",
+            "columnName": "is_current",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryName",
+            "columnName": "diary_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "schoolYear",
+            "columnName": "school_year",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterName",
+            "columnName": "semester_name",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "start",
+            "columnName": "start",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "end",
+            "columnName": "end",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "classId",
+            "columnName": "class_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "unitId",
+            "columnName": "unit_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [
+          {
+            "name": "index_Semesters_student_id_diary_id_semester_id",
+            "unique": true,
+            "columnNames": [
+              "student_id",
+              "diary_id",
+              "semester_id"
+            ],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)"
+          }
+        ],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Exams",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "entryDate",
+            "columnName": "entry_date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "group",
+            "columnName": "group",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "type",
+            "columnName": "type",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "description",
+            "columnName": "description",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacherSymbol",
+            "columnName": "teacher_symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Timetable",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "number",
+            "columnName": "number",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "start",
+            "columnName": "start",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "end",
+            "columnName": "end",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subjectOld",
+            "columnName": "subjectOld",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "group",
+            "columnName": "group",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "room",
+            "columnName": "room",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "roomOld",
+            "columnName": "roomOld",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacherOld",
+            "columnName": "teacherOld",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "info",
+            "columnName": "info",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isStudentPlan",
+            "columnName": "student_plan",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "changes",
+            "columnName": "changes",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "canceled",
+            "columnName": "canceled",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Attendance",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "timeId",
+            "columnName": "time_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "number",
+            "columnName": "number",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "presence",
+            "columnName": "presence",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "absence",
+            "columnName": "absence",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "exemption",
+            "columnName": "exemption",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lateness",
+            "columnName": "lateness",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "excused",
+            "columnName": "excused",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "deleted",
+            "columnName": "deleted",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "excusable",
+            "columnName": "excusable",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "excuseStatus",
+            "columnName": "excuse_status",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "AttendanceSummary",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subjectId",
+            "columnName": "subject_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "month",
+            "columnName": "month",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "presence",
+            "columnName": "presence",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "absence",
+            "columnName": "absence",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "absenceExcused",
+            "columnName": "absence_excused",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "absenceForSchoolReasons",
+            "columnName": "absence_for_school_reasons",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lateness",
+            "columnName": "lateness",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "latenessExcused",
+            "columnName": "lateness_excused",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "exemption",
+            "columnName": "exemption",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Grades",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isRead",
+            "columnName": "is_read",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isNotified",
+            "columnName": "is_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "entry",
+            "columnName": "entry",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "value",
+            "columnName": "value",
+            "affinity": "REAL",
+            "notNull": true
+          },
+          {
+            "fieldPath": "modifier",
+            "columnName": "modifier",
+            "affinity": "REAL",
+            "notNull": true
+          },
+          {
+            "fieldPath": "comment",
+            "columnName": "comment",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "color",
+            "columnName": "color",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "gradeSymbol",
+            "columnName": "grade_symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "description",
+            "columnName": "description",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "weight",
+            "columnName": "weight",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "weightValue",
+            "columnName": "weightValue",
+            "affinity": "REAL",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "GradesSummary",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isPredictedGradeNotified",
+            "columnName": "is_predicted_grade_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isFinalGradeNotified",
+            "columnName": "is_final_grade_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "predictedGradeLastChange",
+            "columnName": "predicted_grade_last_change",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "finalGradeLastChange",
+            "columnName": "final_grade_last_change",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "position",
+            "columnName": "position",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "predictedGrade",
+            "columnName": "predicted_grade",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "finalGrade",
+            "columnName": "final_grade",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "proposedPoints",
+            "columnName": "proposed_points",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "finalPoints",
+            "columnName": "final_points",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "pointsSum",
+            "columnName": "points_sum",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "average",
+            "columnName": "average",
+            "affinity": "REAL",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "GradesStatistics",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `grade` INTEGER NOT NULL, `amount` INTEGER NOT NULL, `is_semester` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "grade",
+            "columnName": "grade",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "amount",
+            "columnName": "amount",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semester",
+            "columnName": "is_semester",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "GradesPointsStatistics",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "others",
+            "columnName": "others",
+            "affinity": "REAL",
+            "notNull": true
+          },
+          {
+            "fieldPath": "student",
+            "columnName": "student",
+            "affinity": "REAL",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Messages",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isNotified",
+            "columnName": "is_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "unreadBy",
+            "columnName": "unread_by",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "readBy",
+            "columnName": "read_by",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "content",
+            "columnName": "content",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "realId",
+            "columnName": "real_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "messageId",
+            "columnName": "message_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "sender",
+            "columnName": "sender_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "senderId",
+            "columnName": "sender_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "recipient",
+            "columnName": "recipient_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "folderId",
+            "columnName": "folder_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "unread",
+            "columnName": "unread",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "removed",
+            "columnName": "removed",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "hasAttachments",
+            "columnName": "has_attachments",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "MessageAttachments",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+        "fields": [
+          {
+            "fieldPath": "realId",
+            "columnName": "real_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "messageId",
+            "columnName": "message_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "oneDriveId",
+            "columnName": "one_drive_id",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "url",
+            "columnName": "url",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "filename",
+            "columnName": "filename",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "real_id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Notes",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isRead",
+            "columnName": "is_read",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isNotified",
+            "columnName": "is_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacherSymbol",
+            "columnName": "teacher_symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "category",
+            "columnName": "category",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "categoryType",
+            "columnName": "category_type",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isPointsShow",
+            "columnName": "is_points_show",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "points",
+            "columnName": "points",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "content",
+            "columnName": "content",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Homework",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isDone",
+            "columnName": "is_done",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "entryDate",
+            "columnName": "entry_date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "content",
+            "columnName": "content",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacherSymbol",
+            "columnName": "teacher_symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "attachments",
+            "columnName": "attachments",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Subjects",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "realId",
+            "columnName": "real_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "LuckyNumbers",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isNotified",
+            "columnName": "is_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "luckyNumber",
+            "columnName": "lucky_number",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "CompletedLesson",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "number",
+            "columnName": "number",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "topic",
+            "columnName": "topic",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacherSymbol",
+            "columnName": "teacher_symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "substitution",
+            "columnName": "substitution",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "absence",
+            "columnName": "absence",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "resources",
+            "columnName": "resources",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "ReportingUnits",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "realId",
+            "columnName": "real_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "shortName",
+            "columnName": "short",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "senderId",
+            "columnName": "sender_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "senderName",
+            "columnName": "sender_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "roles",
+            "columnName": "roles",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Recipients",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "realId",
+            "columnName": "real_id",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "realName",
+            "columnName": "real_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "loginId",
+            "columnName": "login_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "unitId",
+            "columnName": "unit_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "role",
+            "columnName": "role",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "hash",
+            "columnName": "hash",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "MobileDevices",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "deviceId",
+            "columnName": "device_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Teachers",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "classId",
+            "columnName": "class_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "shortName",
+            "columnName": "short_name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "School",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "classId",
+            "columnName": "class_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "address",
+            "columnName": "address",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "contact",
+            "columnName": "contact",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "headmaster",
+            "columnName": "headmaster",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "pedagogue",
+            "columnName": "pedagogue",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Conferences",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "title",
+            "columnName": "title",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "agenda",
+            "columnName": "agenda",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "presentOnConference",
+            "columnName": "present_on_conference",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "conferenceId",
+            "columnName": "conference_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "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, '3a449a55ea73fbfbb7973f1f3f834e10')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
index 6486cab9..50642ddd 100644
--- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
@@ -154,4 +154,8 @@ internal class RepositoryModule {
     @Singleton
     @Provides
     fun provideSchoolInfoDao(database: AppDatabase) = database.schoolDao
+
+    @Singleton
+    @Provides
+    fun provideConferenceDao(database: AppDatabase) = database.conferenceDao
 }
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 ebd5119f..93f22542 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
@@ -10,6 +10,7 @@ import androidx.room.migration.Migration
 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.GradePointsStatisticsDao
@@ -32,6 +33,7 @@ import io.github.wulkanowy.data.db.dao.TimetableDao
 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.GradePointsStatistics
@@ -70,6 +72,7 @@ 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.Migration3
 import io.github.wulkanowy.data.db.migrations.Migration4
 import io.github.wulkanowy.data.db.migrations.Migration5
@@ -103,7 +106,8 @@ import javax.inject.Singleton
         Recipient::class,
         MobileDevice::class,
         Teacher::class,
-        School::class
+        School::class,
+        Conference::class,
     ],
     version = AppDatabase.VERSION_SCHEMA,
     exportSchema = true
@@ -112,7 +116,7 @@ import javax.inject.Singleton
 abstract class AppDatabase : RoomDatabase() {
 
     companion object {
-        const val VERSION_SCHEMA = 27
+        const val VERSION_SCHEMA = 28
 
         fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
             return arrayOf(
@@ -142,6 +146,7 @@ abstract class AppDatabase : RoomDatabase() {
                 Migration25(),
                 Migration26(),
                 Migration27(),
+                Migration28(),
             )
         }
 
@@ -198,4 +203,6 @@ abstract class AppDatabase : RoomDatabase() {
     abstract val teacherDao: TeacherDao
 
     abstract val schoolDao: SchoolDao
+
+    abstract val conferenceDao: ConferenceDao
 }
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
new file mode 100644
index 00000000..4ed9aecf
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/ConferenceDao.kt
@@ -0,0 +1,15 @@
+package io.github.wulkanowy.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import io.github.wulkanowy.data.db.entities.Conference
+import kotlinx.coroutines.flow.Flow
+import javax.inject.Singleton
+
+@Dao
+@Singleton
+interface ConferenceDao : BaseDao<Conference> {
+
+    @Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId")
+    fun loadAll(diaryId: Int, studentId: Int): Flow<List<Conference>>
+}
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
new file mode 100644
index 00000000..8ddcbbb0
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/Conference.kt
@@ -0,0 +1,35 @@
+package io.github.wulkanowy.data.db.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import java.io.Serializable
+import java.time.LocalDateTime
+
+@Entity(tableName = "Conferences")
+data class Conference(
+
+    @ColumnInfo(name = "student_id")
+    val studentId: Int,
+
+    @ColumnInfo(name = "diary_id")
+    val diaryId: Int,
+
+    val title: String,
+
+    val subject: String,
+
+    val agenda: String,
+
+    @ColumnInfo(name = "present_on_conference")
+    val presentOnConference: String,
+
+    @ColumnInfo(name = "conference_id")
+    val conferenceId: Int,
+
+    val date: LocalDateTime
+) : Serializable {
+
+    @PrimaryKey(autoGenerate = true)
+    var id: Long = 0
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt
new file mode 100644
index 00000000..51e7628b
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration28.kt
@@ -0,0 +1,23 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration28 : Migration(27, 28) {
+
+    override fun migrate(database: SupportSQLiteDatabase) {
+        database.execSQL("""
+            CREATE TABLE IF NOT EXISTS Conferences (
+                id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+                student_id INTEGER NOT NULL,
+                diary_id INTEGER NOT NULL,
+                title TEXT NOT NULL,
+                subject TEXT NOT NULL,
+                agenda TEXT NOT NULL,
+                present_on_conference TEXT NOT NULL,
+                conference_id INTEGER NOT NULL,
+                date INTEGER NOT NULL
+            )
+        """)
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceLocal.kt
new file mode 100644
index 00000000..9f56641f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceLocal.kt
@@ -0,0 +1,25 @@
+package io.github.wulkanowy.data.repositories.conference
+
+import io.github.wulkanowy.data.db.dao.ConferenceDao
+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 kotlinx.coroutines.flow.Flow
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ConferenceLocal @Inject constructor(private val conferenceDb: ConferenceDao) {
+
+    fun getConferences(student: Student, semester: Semester): Flow<List<Conference>> {
+        return conferenceDb.loadAll(semester.diaryId, student.studentId)
+    }
+
+    suspend fun saveConferences(items: List<Conference>) {
+        conferenceDb.insertAll(items)
+    }
+
+    suspend fun deleteConferences(items: List<Conference>) {
+        conferenceDb.deleteAll(items)
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceRemote.kt
new file mode 100644
index 00000000..50e869a9
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceRemote.kt
@@ -0,0 +1,31 @@
+package io.github.wulkanowy.data.repositories.conference
+
+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.sdk.Sdk
+import io.github.wulkanowy.utils.init
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ConferenceRemote @Inject constructor(private val sdk: Sdk) {
+
+    suspend fun getConferences(student: Student, semester: Semester): List<Conference> {
+        return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+            .getConferences()
+            .map {
+                it.agenda
+                Conference(
+                    studentId = student.studentId,
+                    diaryId = semester.diaryId,
+                    agenda = it.agenda,
+                    conferenceId = it.id,
+                    date = it.date,
+                    presentOnConference = it.presentOnConference,
+                    subject = it.subject,
+                    title = it.title
+                )
+            }
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceRepository.kt
new file mode 100644
index 00000000..187ecf58
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/conference/ConferenceRepository.kt
@@ -0,0 +1,25 @@
+package io.github.wulkanowy.data.repositories.conference
+
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.utils.networkBoundResource
+import io.github.wulkanowy.utils.uniqueSubtract
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ConferenceRepository @Inject constructor(
+    private val local: ConferenceLocal,
+    private val remote: ConferenceRemote
+) {
+
+    fun getConferences(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
+        shouldFetch = { it.isEmpty() || forceRefresh },
+        query = { local.getConferences(student, semester) },
+        fetch = { remote.getConferences(student, semester) },
+        saveFetchResult = { old, new ->
+            local.deleteConferences(old uniqueSubtract new)
+            local.saveConferences(new uniqueSubtract old)
+        }
+    )
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt
new file mode 100644
index 00000000..c8728614
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceAdapter.kt
@@ -0,0 +1,36 @@
+package io.github.wulkanowy.ui.modules.conference
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import io.github.wulkanowy.data.db.entities.Conference
+import io.github.wulkanowy.databinding.ItemConferenceBinding
+import io.github.wulkanowy.utils.toFormattedString
+import javax.inject.Inject
+
+class ConferenceAdapter @Inject constructor() :
+    RecyclerView.Adapter<ConferenceAdapter.ItemViewHolder>() {
+
+    var items = emptyList<Conference>()
+
+    override fun getItemCount() = items.size
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
+        ItemConferenceBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+    )
+
+    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
+        val item = items[position]
+        with(holder.binding) {
+            conferenceItemDate.text = item.date.toFormattedString("dd.MM.yyyy HH:mm")
+            conferenceItemName.text = item.presentOnConference
+            conferenceItemTitle.text = item.title
+            conferenceItemSubject.text = item.subject
+            conferenceItemContent.text = item.agenda
+            conferenceItemContent.visibility = if (item.agenda.isBlank()) View.GONE else View.VISIBLE
+        }
+    }
+
+    class ItemViewHolder(val binding: ItemConferenceBinding) : RecyclerView.ViewHolder(binding.root)
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt
new file mode 100644
index 00000000..74d93897
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt
@@ -0,0 +1,102 @@
+package io.github.wulkanowy.ui.modules.conference
+
+import android.os.Bundle
+import android.view.View
+import androidx.recyclerview.widget.LinearLayoutManager
+import dagger.hilt.android.AndroidEntryPoint
+import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.Conference
+import io.github.wulkanowy.databinding.FragmentConferenceBinding
+import io.github.wulkanowy.ui.base.BaseFragment
+import io.github.wulkanowy.ui.modules.main.MainView
+import io.github.wulkanowy.ui.widgets.DividerItemDecoration
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class ConferenceFragment : BaseFragment<FragmentConferenceBinding>(R.layout.fragment_conference),
+    ConferenceView, MainView.TitledView {
+
+    @Inject
+    lateinit var presenter: ConferencePresenter
+
+    @Inject
+    lateinit var conferencesAdapter: ConferenceAdapter
+
+    companion object {
+        fun newInstance() = ConferenceFragment()
+    }
+
+    override val isViewEmpty: Boolean
+        get() = conferencesAdapter.items.isEmpty()
+
+    override val titleStringId: Int
+        get() = R.string.conferences_title
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        binding = FragmentConferenceBinding.bind(view)
+        messageContainer = binding.conferenceRecycler
+        presenter.onAttachView(this)
+    }
+
+    override fun initView() {
+        with(binding.conferenceRecycler) {
+            layoutManager = LinearLayoutManager(context)
+            adapter = conferencesAdapter
+            addItemDecoration(DividerItemDecoration(context))
+        }
+
+        with(binding) {
+            conferenceSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
+            conferenceErrorRetry.setOnClickListener { presenter.onRetry() }
+            conferenceErrorDetails.setOnClickListener { presenter.onDetailsClick() }
+        }
+    }
+
+    override fun updateData(data: List<Conference>) {
+        with(conferencesAdapter) {
+            items = data
+            notifyDataSetChanged()
+        }
+    }
+
+    override fun clearData() {
+        with(conferencesAdapter) {
+            items = emptyList()
+            notifyDataSetChanged()
+        }
+    }
+
+    override fun hideRefresh() {
+        binding.conferenceSwipe.isRefreshing = false
+    }
+
+    override fun showProgress(show: Boolean) {
+        binding.conferenceProgress.visibility = if (show) View.VISIBLE else View.GONE
+    }
+
+    override fun showEmpty(show: Boolean) {
+        binding.conferenceEmpty.visibility = if (show) View.VISIBLE else View.GONE
+    }
+
+    override fun showErrorView(show: Boolean) {
+        binding.conferenceError.visibility = if (show) View.VISIBLE else View.GONE
+    }
+
+    override fun setErrorDetails(message: String) {
+        binding.conferenceErrorMessage.text = message
+    }
+
+    override fun enableSwipe(enable: Boolean) {
+        binding.conferenceSwipe.isEnabled = enable
+    }
+
+    override fun showContent(show: Boolean) {
+        binding.conferenceRecycler.visibility = if (show) View.VISIBLE else View.GONE
+    }
+
+    override fun onDestroyView() {
+        presenter.onDetachView()
+        super.onDestroyView()
+    }
+}
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
new file mode 100644
index 00000000..71b412da
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt
@@ -0,0 +1,96 @@
+package io.github.wulkanowy.ui.modules.conference
+
+import io.github.wulkanowy.data.Status
+import io.github.wulkanowy.data.repositories.conference.ConferenceRepository
+import io.github.wulkanowy.data.repositories.semester.SemesterRepository
+import io.github.wulkanowy.data.repositories.student.StudentRepository
+import io.github.wulkanowy.ui.base.BasePresenter
+import io.github.wulkanowy.ui.base.ErrorHandler
+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
+
+class ConferencePresenter @Inject constructor(
+    errorHandler: ErrorHandler,
+    studentRepository: StudentRepository,
+    private val semesterRepository: SemesterRepository,
+    private val conferenceRepository: ConferenceRepository,
+    private val analytics: AnalyticsHelper
+) : BasePresenter<ConferenceView>(errorHandler, studentRepository) {
+
+    private lateinit var lastError: Throwable
+
+    override fun onAttachView(view: ConferenceView) {
+        super.onAttachView(view)
+        view.initView()
+        Timber.i("Conferences view was initialized")
+        errorHandler.showErrorMessage = ::showErrorViewOnError
+        loadData()
+    }
+
+    fun onSwipeRefresh() {
+        loadData(true)
+    }
+
+    fun onRetry() {
+        view?.run {
+            showErrorView(false)
+            showProgress(true)
+        }
+        loadData(true)
+    }
+
+    fun onDetailsClick() {
+        view?.showErrorDetailsDialog(lastError)
+    }
+
+    private fun showErrorViewOnError(message: String, error: Throwable) {
+        view?.run {
+            if (isViewEmpty) {
+                lastError = error
+                setErrorDetails(message)
+                showErrorView(true)
+                showEmpty(false)
+            } else showError(message, error)
+        }
+    }
+
+    private fun loadData(forceRefresh: Boolean = false) {
+        flowWithResourceIn {
+            val student = studentRepository.getCurrentStudent()
+            val semester = semesterRepository.getCurrentSemester(student)
+            conferenceRepository.getConferences(student, semester, forceRefresh)
+        }.onEach {
+            when (it.status) {
+                Status.LOADING -> Timber.i("Loading conference data started")
+                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!!)
+                }
+            }
+        }.afterLoading {
+            view?.run {
+                hideRefresh()
+                showProgress(false)
+                enableSwipe(true)
+            }
+        }.launch()
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt
new file mode 100644
index 00000000..37845a6f
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt
@@ -0,0 +1,29 @@
+package io.github.wulkanowy.ui.modules.conference
+
+import io.github.wulkanowy.data.db.entities.Conference
+import io.github.wulkanowy.ui.base.BaseView
+
+interface ConferenceView : BaseView {
+
+    val isViewEmpty: Boolean
+
+    fun initView()
+
+    fun updateData(data: List<Conference>)
+
+    fun clearData()
+
+    fun hideRefresh()
+
+    fun showEmpty(show: Boolean)
+
+    fun showErrorView(show: Boolean)
+
+    fun setErrorDetails(message: String)
+
+    fun showProgress(show: Boolean)
+
+    fun enableSwipe(enable: Boolean)
+
+    fun showContent(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 1bdcc26f..bf8918fc 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
@@ -9,6 +9,7 @@ import io.github.wulkanowy.R
 import io.github.wulkanowy.databinding.FragmentMoreBinding
 import io.github.wulkanowy.ui.base.BaseFragment
 import io.github.wulkanowy.ui.modules.about.AboutFragment
+import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
 import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
 import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
 import io.github.wulkanowy.ui.modules.main.MainActivity
@@ -53,6 +54,9 @@ class MoreFragment : BaseFragment<FragmentMoreBinding>(R.layout.fragment_more),
     override val mobileDevicesRes: Pair<String, Drawable?>?
         get() = context?.run { getString(R.string.mobile_devices_title) to getCompatDrawable(R.drawable.ic_more_mobile_devices) }
 
+    override val conferencesRes: Pair<String, Drawable?>?
+        get() = context?.run { getString(R.string.conferences_title) to getCompatDrawable(R.drawable.ic_more_conferences) }
+
     override val schoolAndTeachersRes: Pair<String, Drawable?>?
         get() = context?.run { getString(R.string.schoolandteachers_title) to getCompatDrawable((R.drawable.ic_more_schoolandteachers)) }
 
@@ -108,6 +112,10 @@ class MoreFragment : BaseFragment<FragmentMoreBinding>(R.layout.fragment_more),
         (activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance())
     }
 
+    override fun openConferencesView() {
+        (activity as? MainActivity)?.pushView(ConferenceFragment.newInstance())
+    }
+
     override fun openSchoolAndTeachersView() {
         (activity as? MainActivity)?.pushView(SchoolAndTeachersFragment.newInstance())
     }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt
index 6d80f418..d083c981 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MorePresenter.kt
@@ -27,6 +27,7 @@ class MorePresenter @Inject constructor(
                 noteRes?.first -> openNoteView()
                 luckyNumberRes?.first -> openLuckyNumberView()
                 mobileDevicesRes?.first -> openMobileDevicesView()
+                conferencesRes?.first -> openConferencesView()
                 schoolAndTeachersRes?.first -> openSchoolAndTeachersView()
                 settingsRes?.first -> openSettingsView()
                 aboutRes?.first -> openAboutView()
@@ -48,6 +49,7 @@ class MorePresenter @Inject constructor(
                 noteRes,
                 luckyNumberRes,
                 mobileDevicesRes,
+                conferencesRes,
                 schoolAndTeachersRes,
                 settingsRes,
                 aboutRes
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt
index 922afdfd..bb1faeda 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/more/MoreView.kt
@@ -15,6 +15,8 @@ interface MoreView : BaseView {
 
     val mobileDevicesRes: Pair<String, Drawable?>?
 
+    val conferencesRes: Pair<String, Drawable?>?
+
     val schoolAndTeachersRes: Pair<String, Drawable?>?
 
     val settingsRes: Pair<String, Drawable?>?
@@ -41,5 +43,7 @@ interface MoreView : BaseView {
 
     fun openMobileDevicesView()
 
+    fun openConferencesView()
+
     fun openSchoolAndTeachersView()
 }
diff --git a/app/src/main/res/drawable/ic_more_conferences.xml b/app/src/main/res/drawable/ic_more_conferences.xml
new file mode 100644
index 00000000..87be8e05
--- /dev/null
+++ b/app/src/main/res/drawable/ic_more_conferences.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,13.75c-2.34,0 -7,1.17 -7,3.5L2,19h14v-1.75c0,-2.33 -4.66,-3.5 -7,-3.5zM4.34,17c0.84,-0.58 2.87,-1.25 4.66,-1.25s3.82,0.67 4.66,1.25L4.34,17zM9,12c1.93,0 3.5,-1.57 3.5,-3.5S10.93,5 9,5 5.5,6.57 5.5,8.5 7.07,12 9,12zM9,7c0.83,0 1.5,0.67 1.5,1.5S9.83,10 9,10s-1.5,-0.67 -1.5,-1.5S8.17,7 9,7zM16.04,13.81c1.16,0.84 1.96,1.96 1.96,3.44L18,19h4v-1.75c0,-2.02 -3.5,-3.17 -5.96,-3.44zM15,12c1.93,0 3.5,-1.57 3.5,-3.5S16.93,5 15,5c-0.54,0 -1.04,0.13 -1.5,0.35 0.63,0.89 1,1.98 1,3.15s-0.37,2.26 -1,3.15c0.46,0.22 0.96,0.35 1.5,0.35z" />
+</vector>
diff --git a/app/src/main/res/layout/fragment_conference.xml b/app/src/main/res/layout/fragment_conference.xml
new file mode 100644
index 00000000..d7cac878
--- /dev/null
+++ b/app/src/main/res/layout/fragment_conference.xml
@@ -0,0 +1,104 @@
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.modules.conference.ConferenceFragment">
+
+    <me.zhanghai.android.materialprogressbar.MaterialProgressBar
+        android:id="@+id/conferenceProgress"
+        style="@style/Widget.MaterialProgressBar.ProgressBar"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:indeterminate="true" />
+
+    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+        android:id="@+id/conferenceSwipe"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/conferenceRecycler"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            tools:listitem="@layout/item_conference" />
+    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+
+    <LinearLayout
+        android:id="@+id/conferenceEmpty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:orientation="vertical"
+        android:padding="10dp"
+        android:visibility="gone"
+        tools:ignore="UseCompoundDrawables">
+
+        <ImageView
+            android:layout_width="100dp"
+            android:layout_height="100dp"
+            app:srcCompat="@drawable/ic_more_conferences"
+            app:tint="?colorOnBackground"
+            tools:ignore="contentDescription" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="20dp"
+            android:gravity="center"
+            android:text="@string/conference_no_items"
+            android:textSize="20sp" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/conferenceError"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:orientation="vertical"
+        android:visibility="invisible"
+        tools:ignore="UseCompoundDrawables"
+        tools:visibility="invisible">
+
+        <ImageView
+            android:layout_width="100dp"
+            android:layout_height="100dp"
+            app:srcCompat="@drawable/ic_error"
+            app:tint="?colorOnBackground"
+            tools:ignore="contentDescription" />
+
+        <TextView
+            android:id="@+id/conferenceErrorMessage"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="20dp"
+            android:gravity="center"
+            android:padding="8dp"
+            android:text="@string/error_unknown"
+            android:textSize="20sp" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dp"
+            android:gravity="center"
+            android:orientation="horizontal">
+
+            <com.google.android.material.button.MaterialButton
+                android:id="@+id/conferenceErrorDetails"
+                style="@style/Widget.MaterialComponents.Button.OutlinedButton"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="8dp"
+                android:layout_marginRight="8dp"
+                android:text="@string/all_details" />
+
+            <com.google.android.material.button.MaterialButton
+                android:id="@+id/conferenceErrorRetry"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/all_retry" />
+        </LinearLayout>
+    </LinearLayout>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout/item_conference.xml b/app/src/main/res/layout/item_conference.xml
new file mode 100644
index 00000000..4c881f34
--- /dev/null
+++ b/app/src/main/res/layout/item_conference.xml
@@ -0,0 +1,87 @@
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/conference_item_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?selectableItemBackground"
+    tools:context=".ui.modules.conference.ConferenceAdapter">
+
+    <TextView
+        android:id="@+id/conferenceItemDate"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="15dp"
+        android:layout_marginTop="10dp"
+        android:textColor="?android:textColorSecondary"
+        android:textSize="15sp"
+        app:layout_constraintRight_toLeftOf="@+id/conferenceItemName"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="@tools:sample/date/ddmmyy" />
+
+    <TextView
+        android:id="@+id/conferenceItemName"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="10dp"
+        android:layout_marginTop="10dp"
+        android:layout_marginEnd="15dp"
+        android:ellipsize="end"
+        android:gravity="end"
+        android:singleLine="true"
+        android:textSize="13sp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/conferenceItemDate"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="@tools:sample/full_names" />
+
+    <TextView
+        android:id="@+id/conferenceItemTitle"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="5dp"
+        android:layout_marginBottom="5dp"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:textSize="16sp"
+        app:layout_constraintEnd_toEndOf="@id/conferenceItemName"
+        app:layout_constraintStart_toStartOf="@id/conferenceItemDate"
+        app:layout_constraintTop_toBottomOf="@id/conferenceItemDate"
+        app:layout_goneMarginEnd="0dp"
+        tools:text="@tools:sample/lorem" />
+
+    <TextView
+        android:id="@+id/conferenceItemSubject"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="5dp"
+        android:layout_marginBottom="5dp"
+        android:ellipsize="end"
+        android:maxLines="2"
+        android:textSize="16sp"
+        app:layout_constraintBottom_toTopOf="@id/conferenceItemContent"
+        app:layout_constraintEnd_toEndOf="@id/conferenceItemTitle"
+        app:layout_constraintStart_toStartOf="@id/conferenceItemTitle"
+        app:layout_constraintTop_toBottomOf="@id/conferenceItemTitle"
+        app:layout_goneMarginBottom="15dp"
+        app:layout_goneMarginEnd="0dp"
+        tools:text="@tools:sample/lorem" />
+
+    <TextView
+        android:id="@+id/conferenceItemContent"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="5dp"
+        android:layout_marginBottom="15dp"
+        android:ellipsize="end"
+        android:lineSpacingMultiplier="1.2"
+        android:maxLines="2"
+        android:textSize="14sp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="@+id/conferenceItemName"
+        app:layout_constraintStart_toStartOf="@id/conferenceItemDate"
+        app:layout_constraintTop_toBottomOf="@id/conferenceItemSubject"
+        tools:text="@tools:sample/lorem/random"
+        tools:visibility="visible" />
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8e849573..cea6a83d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -325,6 +325,11 @@
     <string name="teacher_no_subject">No subject</string>
 
 
+    <!--Conference-->
+    <string name="conferences_title">Conferences</string>
+    <string name="conference_no_items">No info about conferences</string>
+
+
     <!--Account-->
     <string name="account_add_new">Add account</string>
     <string name="account_logout">Logout</string>

From 83d1d860a61a3efbf256e9f7e7224bcc07479463 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 1 Nov 2020 15:55:36 +0000
Subject: [PATCH 02/34] Bump coil from 1.0.0-rc3 to 1.0.0 (#1006)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index 541aff36..dd71b0db 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -194,7 +194,7 @@ dependencies {
     implementation "fr.bipi.treessence:treessence:0.3.2"
     implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
     implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
-    implementation "io.coil-kt:coil:1.0.0-rc3"
+    implementation "io.coil-kt:coil:1.0.0"
     implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
     implementation 'me.xdrop:fuzzywuzzy:1.3.1'
 

From 6af8263952b580c28af72134f4b2ca409e362a1b Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 1 Nov 2020 15:55:51 +0000
Subject: [PATCH 03/34] Bump firebase-inappmessaging-ktx from 19.1.1 to 19.1.2
 (#1010)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index dd71b0db..c2a4ebcf 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -200,7 +200,7 @@ dependencies {
 
     playImplementation 'com.google.firebase:firebase-analytics:17.6.0'
     playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.1'
-    playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.1"
+    playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.2"
     playImplementation 'com.google.firebase:firebase-messaging:20.3.0'
     playImplementation 'com.google.firebase:firebase-crashlytics:17.2.2'
     playImplementation 'com.google.android.play:core-ktx:1.8.1'

From 7c9e85793b19a4e1d003445226dd27aba295eeca Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 1 Nov 2020 16:15:17 +0000
Subject: [PATCH 04/34] Bump firebase-inappmessaging-display-ktx from 19.1.1 to
 19.1.2 (#1007)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index c2a4ebcf..5e351315 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -199,7 +199,7 @@ dependencies {
     implementation 'me.xdrop:fuzzywuzzy:1.3.1'
 
     playImplementation 'com.google.firebase:firebase-analytics:17.6.0'
-    playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.1'
+    playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.2'
     playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.2"
     playImplementation 'com.google.firebase:firebase-messaging:20.3.0'
     playImplementation 'com.google.firebase:firebase-crashlytics:17.2.2'

From 9ba999feb05e30cd7568c7564ec728c95a965401 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 1 Nov 2020 16:28:32 +0000
Subject: [PATCH 05/34] Bump about_libraries from 8.4.2 to 8.4.3 (#1011)

---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index daec6806..051fce64 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
 buildscript {
     ext {
         kotlin_version = '1.4.10'
-        about_libraries = '8.4.2'
+        about_libraries = '8.4.3'
         hilt_version = "2.29.1-alpha"
     }
     repositories {

From b3109aed0ba98fc24b75b17cdd13a94f426de960 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 1 Nov 2020 16:40:39 +0000
Subject: [PATCH 06/34] Bump firebase-messaging from 20.3.0 to 21.0.0 (#1008)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index 5e351315..c1248afd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -201,7 +201,7 @@ dependencies {
     playImplementation 'com.google.firebase:firebase-analytics:17.6.0'
     playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.2'
     playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.2"
-    playImplementation 'com.google.firebase:firebase-messaging:20.3.0'
+    playImplementation 'com.google.firebase:firebase-messaging:21.0.0'
     playImplementation 'com.google.firebase:firebase-crashlytics:17.2.2'
     playImplementation 'com.google.android.play:core-ktx:1.8.1'
     playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'

From 20644a7a677063210cf32f1150ccf0d307998faf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?= <RafalBO99@outlook.com>
Date: Sun, 1 Nov 2020 19:05:05 +0100
Subject: [PATCH 07/34] Update english strings (#1014)

---
 .../main/res/values/preferences_values.xml    |  2 +-
 app/src/main/res/values/strings.xml           | 22 +++++++++----------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/app/src/main/res/values/preferences_values.xml b/app/src/main/res/values/preferences_values.xml
index d994213c..5f2aec4c 100644
--- a/app/src/main/res/values/preferences_values.xml
+++ b/app/src/main/res/values/preferences_values.xml
@@ -78,7 +78,7 @@
     </string-array>
 
     <string-array name="grade_sorting_mode_entries">
-        <item>Alphabetic</item>
+        <item>Alphabetically</item>
         <item>By date</item>
     </string-array>
     <string-array name="grade_sorting_mode_values" translatable="false">
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index cea6a83d..cc3dbe3c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -26,12 +26,12 @@
 
     <!--Login-->
     <string name="login_header_default">Sign in with the student or parent account</string>
-    <string name="login_header_symbol">Enter the symbol</string>
+    <string name="login_header_symbol">Enter the symbol from the register page</string>
     <string name="login_nickname_hint">Username</string>
     <string name="login_email_hint">Email</string>
     <string name="login_login_pesel_email_hint">Login, PESEL or e-mail</string>
     <string name="login_password_hint">Password</string>
-    <string name="login_host_hint">UONET+ Register</string>
+    <string name="login_host_hint">UONET+ register variant</string>
     <string name="login_type_api">Mobile API</string>
     <string name="login_type_scrapper">Scraper</string>
     <string name="login_type_hybrid">Hybrid</string>
@@ -41,17 +41,17 @@
     <string name="login_symbol_hint">Symbol</string>
     <string name="login_sign_in">Sign in</string>
     <string name="login_invalid_password">Password too short</string>
-    <string name="login_incorrect_password">Login details are incorrect. Make sure the correct UONET+ register is selected</string>
+    <string name="login_incorrect_password">Login details are incorrect. Make sure the correct UONET+ register variation is selected in the field below</string>
     <string name="login_invalid_pin">Invalid PIN</string>
     <string name="login_invalid_token">Invalid token</string>
     <string name="login_expired_token">Token expired</string>
     <string name="login_invalid_email">Invalid email</string>
-    <string name="login_invalid_login">Invalid login</string>
+    <string name="login_invalid_login">Use the assigned login instead of email</string>
     <string name="login_invalid_symbol">Invalid symbol</string>
-    <string name="login_incorrect_symbol">Student not found. Check the symbol</string>
+    <string name="login_incorrect_symbol">Student not found. Validate the symbol and the chosen variation of the UONET+ register</string>
     <string name="login_field_required">This field is required</string>
     <string name="login_duplicate_student">Selected student is already logged in</string>
-    <string name="login_symbol_helper">The symbol can be found on the register page in&#160;<b>Uczeń</b> →&#160;<b>Dostęp Mobilny</b> →&#160;<b>Zarejestruj urządzenie mobilne</b></string>
+    <string name="login_symbol_helper">The symbol can be found on the register page in&#160;<b>Uczeń</b> →&#160;<b>Dostęp Mobilny</b> →&#160;<b>Zarejestruj urządzenie mobilne</b>.\n\nMake sure that you have set the appropriate register variant in the <b>UONET+ register variant</b> field on the previous screen. Wulkanowy does not detect pre-school students at the moment</string>
     <string name="login_select_student">Select students to log in to the application</string>
     <string name="login_advanced">Other options</string>
     <string name="login_advanced_warning_mobile_api">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</string>
@@ -65,7 +65,7 @@
     <string name="login_email_details">Describe details of problem:</string>
     <string name="login_email_subject" translatable="false">Zgłoszenie: Problemy z logowaniem</string>
     <string name="login_email_text" translatable="false">Informacje o aplikacji:\n\nUrządzenie: %1$s\nWersja SDK: %2$s\nWersja aplikacji: %3$s\nDodatkowe informacje: %4$s\nOstatni błąd: %5$s\n\nOpis problemu: </string>
-    <string name="login_recover_warning">Make sure the correct UONET+ register is selected!</string>
+    <string name="login_recover_warning">Make sure you select the correct UONET+ register variation!</string>
     <string name="login_recover_button">I forgot my password</string>
     <string name="login_recover_title">Recover your account</string>
     <string name="login_recover">Recover</string>
@@ -486,12 +486,12 @@
 
     <!--Errors-->
     <string name="error_no_internet">No internet connection</string>
-    <string name="error_timeout">Connection to the register timed out</string>
-    <string name="error_login_failed">Login failed. Try again</string>
-    <string name="error_password_change_required">Password change required</string>
+    <string name="error_timeout">Connection to register failed. Servers can be overloaded. Please try again later</string>
+    <string name="error_login_failed">Loading data failed. Please try again later</string>
+    <string name="error_password_change_required">Register password change required</string>
     <string name="error_service_unavailable">Maintenance underway UONET + register. Try again later</string>
     <string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
-    <string name="error_unknown_app">Unknown application error</string>
+    <string name="error_unknown_app">Unknown application error. Please try again later</string>
     <string name="error_unknown">An unexpected error occurred</string>
     <string name="error_feature_disabled">Feature disabled by your school</string>
     <string name="error_feature_not_available">Feature not available. Login in a mode other than Mobile API</string>

From 57e760844f3aba5a6528e6ea0e4ded7ba8ba5163 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 1 Nov 2020 18:15:52 +0000
Subject: [PATCH 08/34] Bump kotlinx-coroutines-test from 1.3.9 to 1.4.0
 (#1012)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index c1248afd..c4bb798c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -216,7 +216,7 @@ dependencies {
 
     testImplementation "junit:junit:4.13.1"
     testImplementation "io.mockk:mockk:$mockk"
-    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.9'
+    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.0'
 
     androidTestImplementation "androidx.test:core:1.3.0"
     androidTestImplementation "androidx.test:runner:1.3.0"

From 9eb091fbf4cdb0b1d00445ffa0c8914d1d1ae2ee Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 1 Nov 2020 18:16:21 +0000
Subject: [PATCH 09/34] Bump kotlinx-coroutines-android from 1.3.9 to 1.4.0
 (#1005)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index c4bb798c..d1727551 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -147,7 +147,7 @@ dependencies {
     coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
 
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
-    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
+    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'
 
     implementation "androidx.core:core-ktx:1.3.2"
     implementation "androidx.activity:activity-ktx:1.1.0"

From fe191bb0df19c1fe2f28b64efcf8bbf68cedc7e8 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 1 Nov 2020 18:36:55 +0000
Subject: [PATCH 10/34] Bump firebase-analytics from 17.6.0 to 18.0.0 (#1009)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index d1727551..ddb63d47 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -198,7 +198,7 @@ dependencies {
     implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
     implementation 'me.xdrop:fuzzywuzzy:1.3.1'
 
-    playImplementation 'com.google.firebase:firebase-analytics:17.6.0'
+    playImplementation 'com.google.firebase:firebase-analytics:18.0.0'
     playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.2'
     playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.2"
     playImplementation 'com.google.firebase:firebase-messaging:21.0.0'

From ada5854d10bac7d6e9490e149182b31197d04c8c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?= <RafalBO99@outlook.com>
Date: Mon, 2 Nov 2020 17:54:02 +0100
Subject: [PATCH 11/34] New Crowdin updates (#1013)

---
 app/src/main/res/values-cs-rCZ/strings.xml    |  25 ++--
 .../main/res/values-de/preferences_values.xml |   4 +-
 app/src/main/res/values-de/strings.xml        |  69 ++++-----
 app/src/main/res/values-pl/strings.xml        |   9 +-
 app/src/main/res/values-ru/strings.xml        |  35 +++--
 .../main/res/values-uk/preferences_values.xml |   8 +-
 app/src/main/res/values-uk/strings.xml        | 137 +++++++++---------
 7 files changed, 151 insertions(+), 136 deletions(-)

diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml
index 67babcd5..dd474aec 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs-rCZ/strings.xml
@@ -22,12 +22,12 @@
     <string name="grade_subtitle">Semestr %1$d, %2$d/%3$d</string>
     <!--Login-->
     <string name="login_header_default">Přihlaste se pomocí studentského nebo nadřazeného účtu</string>
-    <string name="login_header_symbol">Zadejte symbol</string>
+    <string name="login_header_symbol">Zadejte symbol ze stránky deníku</string>
     <string name="login_nickname_hint">Uživatelské jméno</string>
     <string name="login_email_hint">Email</string>
     <string name="login_login_pesel_email_hint">Přihlášení, číslo PESEL nebo e-mail</string>
     <string name="login_password_hint">Heslo</string>
-    <string name="login_host_hint">Deník UONET+</string>
+    <string name="login_host_hint">Variace deníku UONET+</string>
     <string name="login_type_api">Mobile API</string>
     <string name="login_type_scrapper">Scraper</string>
     <string name="login_type_hybrid">Hybridní</string>
@@ -37,17 +37,17 @@
     <string name="login_symbol_hint">Symbol</string>
     <string name="login_sign_in">Přihlásit</string>
     <string name="login_invalid_password">Toto heslo je příliš krátké</string>
-    <string name="login_incorrect_password">Přihlašovací údaje jsou nesprávné. Zkontrolujte, zda je v poli níže vybrán správný deník UONET+</string>
+    <string name="login_incorrect_password">Přihlašovací údaje jsou nesprávné. Ujistěte se, že je v poli níže vybrána správná variace deníku UONET+</string>
     <string name="login_invalid_pin">Neplatný PIN</string>
     <string name="login_invalid_token">Neplatný token</string>
     <string name="login_expired_token">Platnost tokenu vypršela</string>
     <string name="login_invalid_email">Nesprávná e-mailová adresa</string>
-    <string name="login_invalid_login">Neplatné přihlášení</string>
+    <string name="login_invalid_login">Místo e-mailu použijte přiřazené přihlašovací údaje</string>
     <string name="login_invalid_symbol">Neplatný symbol</string>
-    <string name="login_incorrect_symbol">Student nebyl nalezen. Zkontrolujte symbol</string>
+    <string name="login_incorrect_symbol">Student nebyl nalezen. Zkontrolujte správnost symbolu a vybrané varianty deníku UONET+</string>
     <string name="login_field_required">Toto pole je povinné</string>
     <string name="login_duplicate_student">Vybraný student je již přihlášen</string>
-    <string name="login_symbol_helper">Symbol najdete na stránce deníku v&#160;<b>Uczeń</b> →&#160;<b>Dostęp Mobilny</b> →&#160;<b>Zarejestruj urządzenie mobilne</b></string>
+    <string name="login_symbol_helper">Symbol najdete na stránce deníku v &#160;<b>Uczeń</b> → &#160;<b>Dostęp Mobilny</b> → &#160;<b>Zarejestruj urządzenie mobilne</b>.\n\nUjistěte se, že jste na předchozí obrazovce nastavili správnou variantu deníku do pole <b>Variace deníku UONET+</b>. Wulkanowy v tuto chvíli nezjistí předškolní studenty</string>
     <string name="login_select_student">Vyberte studenty k přihlášení do aplikace</string>
     <string name="login_advanced">Jiné možnosti</string>
     <string name="login_advanced_warning_mobile_api">V tomto režimu nefungují následující: šťastné číslo, statistiky třídy, shrnutí docházky, ospravedlnění nepřítomnosti, absolvované lekce, informace o škole a prohlížení seznamu registrovaných zařízení</string>
@@ -59,7 +59,7 @@
     <string name="login_contact_discord">Discord</string>
     <string name="login_email_intent_title">Poslat e-mail</string>
     <string name="login_email_details">Popište podrobnosti problému:</string>
-    <string name="login_recover_warning">Ujistěte se, že je vybrán správný UONET+ deník!</string>
+    <string name="login_recover_warning">Ujistěte se, že jste vybrali správnou variantu deníku UONET+!</string>
     <string name="login_recover_button">Zapomněl jsem své heslo</string>
     <string name="login_recover_title">Obnovte svůj účet</string>
     <string name="login_recover">Obnovit</string>
@@ -326,6 +326,9 @@
     <string name="teachers_title">Učitelé</string>
     <string name="teacher_no_items">Žádné informace o učitelích</string>
     <string name="teacher_no_subject">Žádný předmět</string>
+    <!--Conference-->
+    <string name="conferences_title">Setkání</string>
+    <string name="conference_no_items">Žádné informace o setkání</string>
     <!--Account-->
     <string name="account_add_new">Přidat účet</string>
     <string name="account_logout">Odhlásit se</string>
@@ -452,12 +455,12 @@
     <string name="update_failed">Aktualizace selhala! Wulkanowy nemusí fungovat správně. Zvažte aktualizaci</string>
     <!--Errors-->
     <string name="error_no_internet">Žádné internetové připojení</string>
-    <string name="error_timeout">Vypršel časový limit připojení k denik</string>
-    <string name="error_login_failed">Přihlášení selhalo. Zkus to znovu</string>
-    <string name="error_password_change_required">Je vyžadována změna hesla</string>
+    <string name="error_timeout">Nelze se připojit ke deníku. Servery mohou být přetíženy. Prosím zkuste to znovu později</string>
+    <string name="error_login_failed">Načítání dat se nezdařilo. Prosím zkuste to znovu později</string>
+    <string name="error_password_change_required">Je vyžadována změna hesla pro deník</string>
     <string name="error_service_unavailable">Probíhá údržba UONET+ deník. Zkuste to později znovu</string>
     <string name="error_unknown_uonet">Neznámá chyba denika UONET+. Prosím zkuste to znovu později</string>
-    <string name="error_unknown_app">Neznámá chyba aplikace</string>
+    <string name="error_unknown_app">Neznámá chyba aplikace. Prosím zkuste to znovu později</string>
     <string name="error_unknown">Došlo k neočekávané chybě</string>
     <string name="error_feature_disabled">Funkce deaktivována vaší školou</string>
     <string name="error_feature_not_available">Funkce není k dispozici. Přihlaste se v jiném režimu než Mobile API</string>
diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml
index 2ee70f62..7226ff2d 100644
--- a/app/src/main/res/values-de/preferences_values.xml
+++ b/app/src/main/res/values-de/preferences_values.xml
@@ -31,8 +31,8 @@
         <item>0,75</item>
     </string-array>
     <string-array name="grade_sorting_mode_entries">
-        <item>Alphabetic</item>
-        <item>By date</item>
+        <item>Alphabetisch</item>
+        <item>Nach Datum</item>
     </string-array>
     <string-array name="grade_color_scheme_entries">
         <item>Dzienniczek+</item>
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 89af346c..d885261c 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -22,12 +22,12 @@
     <string name="grade_subtitle">Semester %1$d, %2$d/%3$d</string>
     <!--Login-->
     <string name="login_header_default">Melden Sie sich mit dem Studenten- oder Elternkonto an</string>
-    <string name="login_header_symbol">Geben Sie das Symbol</string>
+    <string name="login_header_symbol">Geben Sie das Symbol von der Registerseite ein</string>
     <string name="login_nickname_hint">Benutzername</string>
     <string name="login_email_hint">Email</string>
     <string name="login_login_pesel_email_hint">Anmeldung, PESEL oder e-mail</string>
     <string name="login_password_hint">Passwort</string>
-    <string name="login_host_hint">UONET+ Klassenbuch</string>
+    <string name="login_host_hint">UONET+ Registervariante</string>
     <string name="login_type_api">Mobile API</string>
     <string name="login_type_scrapper">Scraper</string>
     <string name="login_type_hybrid">Hybride</string>
@@ -37,17 +37,17 @@
     <string name="login_symbol_hint">Symbol</string>
     <string name="login_sign_in">Anmelden</string>
     <string name="login_invalid_password">Passwort ist zu kurz</string>
-    <string name="login_incorrect_password">Anmeldedaten sind falsch. Stellen Sie sicher, dass das richtige UONET+-Register ausgewählt ist</string>
+    <string name="login_incorrect_password">Anmeldedaten sind falsch. Stellen Sie sicher, dass die richtige UONET+ Registervariation im unteren Feld ausgewählt ist</string>
     <string name="login_invalid_pin">Ungültige PIN</string>
     <string name="login_invalid_token">Ungültige token</string>
     <string name="login_expired_token">Token ist nicht mehr gültig</string>
     <string name="login_invalid_email">Ungültige email</string>
-    <string name="login_invalid_login">Ungültige login</string>
+    <string name="login_invalid_login">Den zugewiesenen Login anstelle von email verwenden</string>
     <string name="login_invalid_symbol">Ungültige symbol</string>
-    <string name="login_incorrect_symbol">Student nicht gefunden. Überprüfen Sie das Symbol</string>
+    <string name="login_incorrect_symbol">Schüler nicht gefunden. Überprüfen Sie das Symbol und die gewählte Variation des UONET+ Registers</string>
     <string name="login_field_required">Dieses Datenfeld ist erforderlich</string>
     <string name="login_duplicate_student">Ausgewählter Student ist bereits angemeldet.</string>
-    <string name="login_symbol_helper">Das Symbol finden Sie auf der Registerseite unter&#160;<b>Uczeń</b> →&#160;<b>Dostęp Mobilny</b> →&#160;<b>Zarejestruj urządzenie mobilne</b></string>
+    <string name="login_symbol_helper">Das Symbol kann auf der Registerseite in&#160;<b>Uczeń</b>→&#160;<b>Dostęp Mobilny</b> →&#160;<b>Zarejestruj urządzenie mobilne</b>gefunden werden.\n\nStellen Sie sicher, dass Sie die entsprechende Registervariante im Feld <b>UONET+ Registervariante</b> auf dem vorherigen Bildschirm festgelegt haben. Wulkanowy erkennt zur Zeit keine Vorschulstudenten</string>
     <string name="login_select_student">Wählen Sie die Studenten aus, die sich bei der Anwendung anmelden sollen.</string>
     <string name="login_advanced">Andere Optionen</string>
     <string name="login_advanced_warning_mobile_api">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</string>
@@ -59,7 +59,7 @@
     <string name="login_contact_discord">Discord</string>
     <string name="login_email_intent_title">email senden</string>
     <string name="login_email_details">Beschreiben Sie die Details des Problems:</string>
-    <string name="login_recover_warning">Stellen Sie sicher, dass das richtige UONET + Klassenbuch ausgewählt ist!</string>
+    <string name="login_recover_warning">Stellen Sie sicher, dass Sie die richtige UONET+ Registervariation wählen!</string>
     <string name="login_recover_button">Ich habe mein Passwort vergessen.</string>
     <string name="login_recover_title">Ihr Konto wiederherstellen</string>
     <string name="login_recover">Wiederherstellen</string>
@@ -154,8 +154,8 @@
     <string name="attendance_excused_lateness">Entschuldigte Verspätung</string>
     <string name="attendance_unexcused_lateness">Unentschuldigte Verspätung</string>
     <string name="attendance_present">Anwesend</string>
-    <string name="attendance_deleted">Deleted</string>
-    <string name="attendance_unknown">Unknown</string>
+    <string name="attendance_deleted">Entfernt</string>
+    <string name="attendance_unknown">Unbekannt</string>
     <string name="attendance_number">Lektion Nummer</string>
     <string name="attendance_no_items">Keine Einträgen</string>
     <plurals name="attendance_number_absences">
@@ -226,29 +226,29 @@
     </plurals>
     <!--Praise-->
     <plurals name="praise_number_item">
-        <item quantity="one">%d praise</item>
-        <item quantity="other">%d praises</item>
+        <item quantity="one">%d Lob</item>
+        <item quantity="other">%d Lob</item>
     </plurals>
     <plurals name="praise_new_items">
-        <item quantity="one">New praise</item>
-        <item quantity="other">New praises</item>
+        <item quantity="one">Neues Lob</item>
+        <item quantity="other">Neues Lob</item>
     </plurals>
     <plurals name="praise_notify_new_items">
-        <item quantity="one">You received %1$d praise</item>
-        <item quantity="other">You received %1$d praises</item>
+        <item quantity="one">Du hast %1$d Lob erhalten</item>
+        <item quantity="other">Du hast %1$d Lob erhalten</item>
     </plurals>
     <!--Neutral notes-->
     <plurals name="neutral_note_number_item">
-        <item quantity="one">%d neutral note</item>
-        <item quantity="other">%d neutral notes</item>
+        <item quantity="one">%d neutrale Notiz</item>
+        <item quantity="other">%d neutrale Notizen</item>
     </plurals>
     <plurals name="neutral_note_new_items">
-        <item quantity="one">New neutral note</item>
-        <item quantity="other">New neutral notes</item>
+        <item quantity="one">Neue neutrale Notiz</item>
+        <item quantity="other">Neue neutrale Notizen</item>
     </plurals>
     <plurals name="neutral_note_notify_new_items">
-        <item quantity="one">You received %1$d neutral note</item>
-        <item quantity="other">You received %1$d neutral notes</item>
+        <item quantity="one">Du hast %1$d neutrale Notiz erhalten</item>
+        <item quantity="other">Du hast %1$d neutrale Notizen erhalten</item>
     </plurals>
     <!--Homework-->
     <string name="homework_no_items">Keine Informationen über Hausaufgaben</string>
@@ -286,6 +286,9 @@
     <string name="teachers_title">Lehrerinnen und Lehrer</string>
     <string name="teacher_no_items">Keine Informationen über Lehrer</string>
     <string name="teacher_no_subject">Kein Thema</string>
+    <!--Conference-->
+    <string name="conferences_title">Sitzungen</string>
+    <string name="conference_no_items">Keine Informationen über Sitzungen</string>
     <!--Account-->
     <string name="account_add_new">Konto hinzufügen</string>
     <string name="account_logout">Abmelden</string>
@@ -355,12 +358,12 @@
     <string name="pref_view_app_theme">Thema der Anwendung</string>
     <string name="pref_view_expand_grade">Noten erweitern</string>
     <string name="pref_view_timetable_show_timers">Aktuelle Lektion im Stundenplan markieren</string>
-    <string name="pref_view_timetable_show_groups">Show groups next to subjects in timetable</string>
+    <string name="pref_view_timetable_show_groups">Zeige Gruppen neben Themen im Zeitplan</string>
     <string name="pref_view_grade_statistics_list">Liste der Diagramme in Klassenbewertungen anzeigen</string>
     <string name="pref_view_timetable_show_whole_class">Unterricht der ganzen Klasse anzeigen</string>
-    <string name="pref_view_subjects_without_grades">Show subjects without grades in Grades</string>
+    <string name="pref_view_subjects_without_grades">Zeigen Sie Themen ohne Noten in Noten</string>
     <string name="pref_view_grade_color_scheme">Farbschema der Noten</string>
-    <string name="pref_view_grade_sorting_mode">Subjects sorting in \"Grades\"</string>
+    <string name="pref_view_grade_sorting_mode">Themen sortieren in \"Noten\"</string>
     <string name="pref_view_app_language">App Sprache</string>
     <string name="pref_notify_header">Benachrichtigungen</string>
     <string name="pref_notify_switch">Benachrichtigungen anzeigen</string>
@@ -406,18 +409,18 @@
     <string name="all_copied">Kopiert</string>
     <string name="all_undo">lösen</string>
     <!--Update helper-->
-    <string name="update_download_started">Download of updates has started…</string>
-    <string name="update_download_success">An update has just been downloaded.</string>
-    <string name="update_download_success_button">Restart</string>
-    <string name="update_failed">Update failed! Wulkanowy may not function properly. Consider updating</string>
+    <string name="update_download_started">Download der Updates wurde gestartet…</string>
+    <string name="update_download_success">Ein Update wurde gerade heruntergeladen.</string>
+    <string name="update_download_success_button">Neustart</string>
+    <string name="update_failed">Update fehlgeschlagen! Wulkanowy funktioniert möglicherweise nicht richtig. Überlegen Sie die Aktualisierung</string>
     <!--Errors-->
     <string name="error_no_internet">Keine Internetverbindung</string>
-    <string name="error_timeout">Das Zeitlimit für die Verbindung zum Klassenbuch ist abgelaufen</string>
-    <string name="error_login_failed">Login failed. Try again</string>
-    <string name="error_password_change_required">Passwortänderung erforderlich</string>
+    <string name="error_timeout">Registrierungsverbindung fehlgeschlagen. Server können überlastet sein. Bitte versuchen Sie es später noch einmal</string>
+    <string name="error_login_failed">Das Laden der Daten ist fehlgeschlagen. Bitte versuchen Sie es später noch einmal</string>
+    <string name="error_password_change_required">Passwortänderung für Registrierung erforderlich</string>
     <string name="error_service_unavailable">Wartung im Gange UONET + Klassenbuch. Versuchen Sie es später noch einmal</string>
-    <string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
-    <string name="error_unknown_app">Unknown application error</string>
+    <string name="error_unknown_uonet">Unbekannter UONET + Registerfehler. Versuchen Sie es später erneut</string>
+    <string name="error_unknown_app">Unbekannter Anwendungsfehler. Bitte versuchen Sie es später noch einmal</string>
     <string name="error_unknown">Ein unerwarteter Fehler ist aufgetreten</string>
     <string name="error_feature_disabled">Funktion, die von Ihrer Schule deaktiviert wurde</string>
     <string name="error_feature_not_available">Feature in diesem Modus nicht verfügbar</string>
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 99fd05db..67f8bd1b 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -168,7 +168,7 @@
     <string name="attendance_excused_lateness">Spóźnienie usprawiedliwione</string>
     <string name="attendance_unexcused_lateness">Spóźnienie nieusprawiedliwione</string>
     <string name="attendance_present">Obecność</string>
-    <string name="attendance_deleted">Usunięty</string>
+    <string name="attendance_deleted">Usunięto</string>
     <string name="attendance_unknown">Nieznany</string>
     <string name="attendance_number">Numer lekcji</string>
     <string name="attendance_no_items">Brak wpisów</string>
@@ -326,6 +326,9 @@
     <string name="teachers_title">Nauczyciele</string>
     <string name="teacher_no_items">Brak informacji o nauczycielach</string>
     <string name="teacher_no_subject">Brak przedmiotu</string>
+    <!--Conference-->
+    <string name="conferences_title">Zebrania</string>
+    <string name="conference_no_items">Brak informacji o zebraniach</string>
     <!--Account-->
     <string name="account_add_new">Dodaj konto</string>
     <string name="account_logout">Wyloguj</string>
@@ -398,9 +401,9 @@
     <string name="pref_view_timetable_show_groups">Pokazuj grupę obok przedmiotu na planie</string>
     <string name="pref_view_grade_statistics_list">Pokazuj listę wykresów w ocenach klasy</string>
     <string name="pref_view_timetable_show_whole_class">Pokazuj lekcje całej klasy</string>
-    <string name="pref_view_subjects_without_grades">Pokazuj przedmioty bez ocen w Oceny</string>
+    <string name="pref_view_subjects_without_grades">Pokazuj przedmioty bez ocen w Ocenach</string>
     <string name="pref_view_grade_color_scheme">Schemat kolorów ocen</string>
-    <string name="pref_view_grade_sorting_mode">Sortowanie przedmiotów w \"Oceny\"</string>
+    <string name="pref_view_grade_sorting_mode">Sortowanie przedmiotów w Ocenach</string>
     <string name="pref_view_app_language">Język aplikacji</string>
     <string name="pref_notify_header">Powiadomienia</string>
     <string name="pref_notify_switch">Pokazuj powiadomienia</string>
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 7a10553d..5e260d99 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -22,12 +22,12 @@
     <string name="grade_subtitle">%1$d семестр, %2$d/%3$d</string>
     <!--Login-->
     <string name="login_header_default">Авторизируйтесь при помощи аккаунта ученика или родителя</string>
-    <string name="login_header_symbol">Впишите \"symbol\"</string>
+    <string name="login_header_symbol">Введите символ со страницы регистрации</string>
     <string name="login_nickname_hint">Имя пользователя</string>
     <string name="login_email_hint">Электронная почта</string>
     <string name="login_login_pesel_email_hint">Логин, PESEL или электронная почта</string>
     <string name="login_password_hint">Пароль</string>
-    <string name="login_host_hint">Дневник UONET+</string>
+    <string name="login_host_hint">Разновидностью бревна UONET+</string>
     <string name="login_type_api">Mobile API</string>
     <string name="login_type_scrapper">Scraper</string>
     <string name="login_type_hybrid">Hybrid</string>
@@ -37,17 +37,17 @@
     <string name="login_symbol_hint">Symbol</string>
     <string name="login_sign_in">Войти</string>
     <string name="login_invalid_password">Слишком короткий пароль</string>
-    <string name="login_incorrect_password">Указаны неверные данные. Убедитесь, что вы выбрали нужный дневник</string>
+    <string name="login_incorrect_password">Данные для входа неверны. Убедитесь, что в поле ниже выбран правильный вариант регистра UONET+</string>
     <string name="login_invalid_pin">Неправильный PIN</string>
     <string name="login_invalid_token">Неправильный token</string>
     <string name="login_expired_token">Токен просрочен</string>
     <string name="login_invalid_email">Неверный адрес электронной почты</string>
-    <string name="login_invalid_login">Неправильный логин</string>
+    <string name="login_invalid_login">Используйте назначенный логин вместо электронной почты</string>
     <string name="login_invalid_symbol">Неправильный symbol</string>
-    <string name="login_incorrect_symbol">Не удалось найти ученика. Проверьте \"symbol\"</string>
+    <string name="login_incorrect_symbol">Студент не найден. Подтвердите символ и выбранный вариант регистра UONET+</string>
     <string name="login_field_required">Обязательное поле</string>
     <string name="login_duplicate_student">Данный ученик уже авторизован</string>
-    <string name="login_symbol_helper">Вы можете найти \"symbol\" на странице VULCAN по пути <b>Uczeń</b> →&#160;<b>Dostęp Mobilny</b> →&#160;<b>Zarejestruj urządzenie mobilne</b></string>
+    <string name="login_symbol_helper">Этот символ можно найти на странице регистрации в &#160;<b>Uczeń</b> → &#160;<b>Dostęp Mobilny</b> → &#160;<b>Zarejestruj urządzenie mobilne</b>.\n\nУбедитесь, что вы установили соответствующий вариант регистра в поле <b>Разновидностью бревна UONET+</b> на предыдущем экране. Wulkanowy на данный момент не обнаруживает дошкольников</string>
     <string name="login_select_student">Выберите учеников для авторизации в приложении</string>
     <string name="login_advanced">Другие варианты</string>
     <string name="login_advanced_warning_mobile_api">В этом режиме не работают: счастливый номер, статистика класса по оценкам, статистика посещаемости и уроков, информация о школе и список зарегистрированных устройств</string>
@@ -59,7 +59,7 @@
     <string name="login_contact_discord">Discord</string>
     <string name="login_email_intent_title">Отправить письмо</string>
     <string name="login_email_details">Опишите детали проблемы:</string>
-    <string name="login_recover_warning">Убедитесь, что выбран нужный дневник!</string>
+    <string name="login_recover_warning">Убедитесь, что вы выбрали правильный вариант регистра UONET+!</string>
     <string name="login_recover_button">Забыли пароль?</string>
     <string name="login_recover_title">Восстановите свой аккаунт</string>
     <string name="login_recover">Восстановить</string>
@@ -326,6 +326,9 @@
     <string name="teachers_title">Учителя</string>
     <string name="teacher_no_items">Нет информации о учителях</string>
     <string name="teacher_no_subject">Нет предмета</string>
+    <!--Conference-->
+    <string name="conferences_title">Встречи</string>
+    <string name="conference_no_items">Нет информации о встречах</string>
     <!--Account-->
     <string name="account_add_new">Добавить аккаунт</string>
     <string name="account_logout">Выйти</string>
@@ -446,18 +449,18 @@
     <string name="all_copied">Скопировано</string>
     <string name="all_undo">Отменить</string>
     <!--Update helper-->
-    <string name="update_download_started">Download of updates has started…</string>
-    <string name="update_download_success">An update has just been downloaded.</string>
-    <string name="update_download_success_button">Restart</string>
-    <string name="update_failed">Update failed! Wulkanowy may not function properly. Consider updating</string>
+    <string name="update_download_started">Загрузка обновлений началась…</string>
+    <string name="update_download_success">Только что было скачано обновление.</string>
+    <string name="update_download_success_button">Перезапустить</string>
+    <string name="update_failed">Не удалось обновить! Wulkanowy может работать некорректно. Рассмотрите возможность обновления</string>
     <!--Errors-->
     <string name="error_no_internet">Нет интернет-подключения</string>
-    <string name="error_timeout">Слишком долгое ожидание соединения с дневником</string>
-    <string name="error_login_failed">Login failed. Try again</string>
-    <string name="error_password_change_required">Требуется смена пароля</string>
+    <string name="error_timeout">Не удалось подключиться к регистрации. Серверы могут быть перегружены. Пожалуйста, повторите попытку позже</string>
+    <string name="error_login_failed">Не удалось загрузить данные. Пожалуйста, повторите попытку позже</string>
+    <string name="error_password_change_required">Необходимо изменить пароль реестра</string>
     <string name="error_service_unavailable">Технический перерыв в журнале UONET + продолжается. Попробуйте позже</string>
-    <string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
-    <string name="error_unknown_app">Unknown application error</string>
+    <string name="error_unknown_uonet">Неизвестная ошибка UONET + регистр. Попробуйте позже</string>
+    <string name="error_unknown_app">Неизвестная ошибка приложения. Пожалуйста, повторите попытку позже</string>
     <string name="error_unknown">Произошла неожиданная ошибка</string>
     <string name="error_feature_disabled">Функция была выключена школой</string>
     <string name="error_feature_not_available">Функция не доступна в этом режиме</string>
diff --git a/app/src/main/res/values-uk/preferences_values.xml b/app/src/main/res/values-uk/preferences_values.xml
index 0ff8bfd0..02b6ccf8 100644
--- a/app/src/main/res/values-uk/preferences_values.xml
+++ b/app/src/main/res/values-uk/preferences_values.xml
@@ -31,8 +31,8 @@
         <item>0,75</item>
     </string-array>
     <string-array name="grade_sorting_mode_entries">
-        <item>Alphabetic</item>
-        <item>By date</item>
+        <item>За алфавітом</item>
+        <item>За датою</item>
     </string-array>
     <string-array name="grade_color_scheme_entries">
         <item>Dzienniczek+</item>
@@ -41,8 +41,8 @@
     </string-array>
     <string-array name="grade_average_mode_entries">
         <item>Середня оцінка з 2 семестру</item>
-        <item>Average of grades from both semesters</item>
-        <item>Average of grades from the whole year</item>
+        <item>Середнє оцінювання за обидва семестри</item>
+        <item>Середнє оцінювання за весь рік</item>
     </string-array>
     <string-array name="timetable_show_whole_class_entries">
         <item>Не показувати</item>
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 4a15db9f..48da2784 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -22,12 +22,12 @@
     <string name="grade_subtitle">%1$d семестр, %2$d/%3$d</string>
     <!--Login-->
     <string name="login_header_default">Авторизуйтеся за допомогою аккаунта учня або батька</string>
-    <string name="login_header_symbol">Впишіть \"symbol\"</string>
+    <string name="login_header_symbol">Введіть символ зі сторінки реєстру</string>
     <string name="login_nickname_hint">Ім\'я користувача</string>
     <string name="login_email_hint">Електронна пошта</string>
     <string name="login_login_pesel_email_hint">Логін, PESEL або електронна пошта</string>
     <string name="login_password_hint">Пароль</string>
-    <string name="login_host_hint">Щоденник</string>
+    <string name="login_host_hint">UONET + варіант реєстрації</string>
     <string name="login_type_api">Мobile API</string>
     <string name="login_type_scrapper">Scraper</string>
     <string name="login_type_hybrid">Hybrid</string>
@@ -37,17 +37,17 @@
     <string name="login_symbol_hint">Symbol</string>
     <string name="login_sign_in">Увійти</string>
     <string name="login_invalid_password">Занадто короткий пароль</string>
-    <string name="login_incorrect_password">Вказані невірні дані. Впевніться, що ви увібрали потрібний щоденник</string>
+    <string name="login_incorrect_password">Дані для входу неправильні. Переконайтеся, що у полі нижче вказано правильний варіант реєстрації UONET+</string>
     <string name="login_invalid_pin">Неправильний PIN</string>
     <string name="login_invalid_token">Неправильний token</string>
     <string name="login_expired_token">Минув термін дії токену</string>
     <string name="login_invalid_email">Недійсна адреса електронної пошти</string>
-    <string name="login_invalid_login">Неправильний логін</string>
+    <string name="login_invalid_login">Використовуйте призначений логін замість електронної пошти</string>
     <string name="login_invalid_symbol">Неправильний symbol</string>
-    <string name="login_incorrect_symbol">Не вдалося знайти учня. Будь ласка, перевірте \"symbol\"</string>
+    <string name="login_incorrect_symbol">Студента не знайдено Перевірте символ та обраний варіант реєстру UONET+</string>
     <string name="login_field_required">Обов\'язкове поле</string>
     <string name="login_duplicate_student">Даного учня вже авторизовано</string>
-    <string name="login_symbol_helper">Ви можете знайти \"symbol\" на сторінцi VULCAN стежкою <b>Uczeń</b> →&#160;<b>Dostęp Mobilny</b> →&#160;<b>Zarejestruj urządzenie mobilne</b></string>
+    <string name="login_symbol_helper">Символ можна знайти на сторінці реєстру в &#160; <b> Uczeń </b> → &#160; <b> Dostęp Mobilny </b> → &#160; <b> Zarejestruj urządzenie mobilne </b>.\n\nПереконайтесь, що ви встановили відповідний варіант реєстру в полі <b> UONET + варіант реєстрації </b> на попередньому екрані. На даний момент Wulkanowy не виявляє учнів дошкільних закладів</string>
     <string name="login_select_student">Виберіть учнів для авторизації в додатку</string>
     <string name="login_advanced">Інші варіанти</string>
     <string name="login_advanced_warning_mobile_api">У цьому режимі не працюють: щасливий номер, статистика класу по оцінкам, статистика відвідуваності і уроків, інформація про школу і список зареєстрованних пристроїв</string>
@@ -59,7 +59,7 @@
     <string name="login_contact_discord">Discord</string>
     <string name="login_email_intent_title">Відправити лист</string>
     <string name="login_email_details">Опишіть деталі помилки:</string>
-    <string name="login_recover_warning">Переконайтесь, що обрано потрібний щоденник!</string>
+    <string name="login_recover_warning">Переконайтеся, що ви вибрали правильний варіант реєстрації UONET+!</string>
     <string name="login_recover_button">Забули пароль?</string>
     <string name="login_recover_title">Відновіть свій аккаунт</string>
     <string name="login_recover">Відновити</string>
@@ -108,16 +108,16 @@
         <item quantity="other">Нові оцінки</item>
     </plurals>
     <plurals name="grade_new_items_predicted">
-        <item quantity="one">New predicted grade</item>
-        <item quantity="few">New predicted grades</item>
-        <item quantity="many">New predicted grades</item>
-        <item quantity="other">New predicted grades</item>
+        <item quantity="one">Нова прогнозована оцінка</item>
+        <item quantity="few">Нові прогнозовані оцінки</item>
+        <item quantity="many">Нові прогнозовані оцінки</item>
+        <item quantity="other">Нові прогнозовані оцінки</item>
     </plurals>
     <plurals name="grade_new_items_final">
-        <item quantity="one">New final grade</item>
-        <item quantity="few">New final grades</item>
-        <item quantity="many">New final grades</item>
-        <item quantity="other">New final grades</item>
+        <item quantity="one">Нова підсумкова оцінка</item>
+        <item quantity="few">Нові підсумкові оцінки</item>
+        <item quantity="many">Нові підсумкові оцінки</item>
+        <item quantity="other">Нові підсумкові оцінки</item>
     </plurals>
     <plurals name="grade_notify_new_items">
         <item quantity="one">Ви отримали %1$d оцінку</item>
@@ -126,16 +126,16 @@
         <item quantity="other">Ви отримали %1$d оцінок</item>
     </plurals>
     <plurals name="grade_notify_new_items_predicted">
-        <item quantity="one">You received %1$d predicted grade</item>
-        <item quantity="few">You received %1$d predicted grades</item>
-        <item quantity="many">You received %1$d predicted grades</item>
-        <item quantity="other">You received %1$d predicted grades</item>
+        <item quantity="one">Ви отримали %1$d нову прогнозовану оцінку</item>
+        <item quantity="few">Ви отримали %1$d нові прогнозовані оцінки</item>
+        <item quantity="many">Ви отримали %1$d нових прогнозованих оцінок</item>
+        <item quantity="other">Ви отримали %1$d нових прогнозованих оцінок</item>
     </plurals>
     <plurals name="grade_notify_new_items_final">
-        <item quantity="one">You received %1$d final grade</item>
-        <item quantity="few">You received %1$d final grades</item>
-        <item quantity="many">You received %1$d final grades</item>
-        <item quantity="other">You received %1$d final grades</item>
+        <item quantity="one">Ви отримали %1$d нову підсумкову оцінку</item>
+        <item quantity="few">Ви отримали %1$d нові підсумкові оцінки</item>
+        <item quantity="many">Ви отримали %1$d нових підсумкових оцінок</item>
+        <item quantity="other">Ви отримали %1$d нових підсумкових оцінок</item>
     </plurals>
     <!--Timetable-->
     <string name="timetable_lesson">Урок</string>
@@ -168,8 +168,8 @@
     <string name="attendance_excused_lateness">Спізнення з поважних причин</string>
     <string name="attendance_unexcused_lateness">Спізнення з не поважних причин</string>
     <string name="attendance_present">Присутність</string>
-    <string name="attendance_deleted">Deleted</string>
-    <string name="attendance_unknown">Unknown</string>
+    <string name="attendance_deleted">Вилучено</string>
+    <string name="attendance_unknown">Невідомо</string>
     <string name="attendance_number">Номер уроку</string>
     <string name="attendance_no_items">Брак записів</string>
     <plurals name="attendance_number_absences">
@@ -206,8 +206,8 @@
     <string name="message_move_to_bin">Перемістити у кошик</string>
     <string name="message_delete_forever">Видалити назавжди</string>
     <string name="message_delete_success">Повідомлення було успішно видалено</string>
-    <string name="message_share">Share</string>
-    <string name="message_print">Print</string>
+    <string name="message_share">Поділіться</string>
+    <string name="message_print">Друк</string>
     <string name="message_subject">Тема</string>
     <string name="message_content">Зміст</string>
     <string name="message_send_successful">Повідомлення було успішно відправлено</string>
@@ -254,41 +254,41 @@
     </plurals>
     <!--Praise-->
     <plurals name="praise_number_item">
-        <item quantity="one">%d praise</item>
-        <item quantity="few">%d praises</item>
-        <item quantity="many">%d praises</item>
-        <item quantity="other">%d praises</item>
+        <item quantity="one">%d похвалили</item>
+        <item quantity="few">%d похвали</item>
+        <item quantity="many">%d похвал</item>
+        <item quantity="other">%d похвал</item>
     </plurals>
     <plurals name="praise_new_items">
-        <item quantity="one">New praise</item>
-        <item quantity="few">New praises</item>
-        <item quantity="many">New praises</item>
-        <item quantity="other">New praises</item>
+        <item quantity="one">Нова похвала</item>
+        <item quantity="few">Нові похвали</item>
+        <item quantity="many">Нові похвали</item>
+        <item quantity="other">Нові похвали</item>
     </plurals>
     <plurals name="praise_notify_new_items">
-        <item quantity="one">You received %1$d praise</item>
-        <item quantity="few">You received %1$d praises</item>
-        <item quantity="many">You received %1$d praises</item>
-        <item quantity="other">You received %1$d praises</item>
+        <item quantity="one">Ви отримали %1$d нову похвалу</item>
+        <item quantity="few">Ви отримали %1$d нові похвали</item>
+        <item quantity="many">Ви отримали %1$d нових похвал</item>
+        <item quantity="other">Ви отримали %1$d нових похвал</item>
     </plurals>
     <!--Neutral notes-->
     <plurals name="neutral_note_number_item">
-        <item quantity="one">%d neutral note</item>
-        <item quantity="few">%d neutral notes</item>
-        <item quantity="many">%d neutral notes</item>
-        <item quantity="other">%d neutral notes</item>
+        <item quantity="one">%d нейтральна нота</item>
+        <item quantity="few">%d нейтральні ноти</item>
+        <item quantity="many">%d нейтральних нот</item>
+        <item quantity="other">%d нейтральних нот</item>
     </plurals>
     <plurals name="neutral_note_new_items">
-        <item quantity="one">New neutral note</item>
-        <item quantity="few">New neutral notes</item>
-        <item quantity="many">New neutral notes</item>
-        <item quantity="other">New neutral notes</item>
+        <item quantity="one">Нова нейтральна нота</item>
+        <item quantity="few">Нова нейтральна нота</item>
+        <item quantity="many">Нова нейтральна нота</item>
+        <item quantity="other">Нові нейтральні ноти</item>
     </plurals>
     <plurals name="neutral_note_notify_new_items">
-        <item quantity="one">You received %1$d neutral note</item>
-        <item quantity="few">You received %1$d neutral notes</item>
-        <item quantity="many">You received %1$d neutral notes</item>
-        <item quantity="other">You received %1$d neutral notes</item>
+        <item quantity="one">Ви отримали %1$d нову нейтральну ноту</item>
+        <item quantity="few">Ви отримали %1$d нові нейтральні ноти</item>
+        <item quantity="many">Ви отримали %1$d нових нейтральних нот</item>
+        <item quantity="other">Ви отримали %1$d нових нейтральних нот</item>
     </plurals>
     <!--Homework-->
     <string name="homework_no_items">Брак домашніх завдань</string>
@@ -326,15 +326,18 @@
     <string name="teachers_title">Вчителі</string>
     <string name="teacher_no_items">Брак інформації про вчителів</string>
     <string name="teacher_no_subject">Брак предмету</string>
+    <!--Conference-->
+    <string name="conferences_title">Зустрічі</string>
+    <string name="conference_no_items">Немає інформації про зустрічі</string>
     <!--Account-->
     <string name="account_add_new">Додати аккаунт</string>
     <string name="account_logout">Вийти</string>
     <string name="account_confirm">Ви впевнені, що хочете вийти з цього аккаунту?</string>
     <string name="account_logout_student">Вийти з аккаунту учня</string>
-    <string name="account_type_student">Student account</string>
-    <string name="account_type_parent">Parent account</string>
-    <string name="account_login_mobile_api">Mobile API mode</string>
-    <string name="account_login_hybrid">Hybrid mode</string>
+    <string name="account_type_student">Студентський рахунок</string>
+    <string name="account_type_parent">Головний рахунок</string>
+    <string name="account_login_mobile_api">Режим мобільного API</string>
+    <string name="account_login_hybrid">Гібридний режим</string>
     <!--About-->
     <string name="about_version">Версія додатка</string>
     <string name="about_contributor">Розробники</string>
@@ -395,12 +398,12 @@
     <string name="pref_view_app_theme">Тема додатку</string>
     <string name="pref_view_expand_grade">Більше оцінок</string>
     <string name="pref_view_timetable_show_timers">Позначити поточний урок у розкладі</string>
-    <string name="pref_view_timetable_show_groups">Show groups next to subjects in timetable</string>
+    <string name="pref_view_timetable_show_groups">Покажіть групи поруч із предметами в розкладі</string>
     <string name="pref_view_grade_statistics_list">Показувати діаграми в оцінках класу</string>
     <string name="pref_view_timetable_show_whole_class">Показати уроки всього класу</string>
-    <string name="pref_view_subjects_without_grades">Show subjects without grades in Grades</string>
+    <string name="pref_view_subjects_without_grades">Показуйте предмети без оцінок у оцінках</string>
     <string name="pref_view_grade_color_scheme">Схема кольорів оцінок</string>
-    <string name="pref_view_grade_sorting_mode">Subjects sorting in \"Grades\"</string>
+    <string name="pref_view_grade_sorting_mode">Сортування предметів за \"Оцінками\"</string>
     <string name="pref_view_app_language">Мова додатку</string>
     <string name="pref_notify_header">Повідомлення</string>
     <string name="pref_notify_switch">Показувати повідомлення</string>
@@ -446,18 +449,18 @@
     <string name="all_copied">Скопійовано</string>
     <string name="all_undo">Відмінити</string>
     <!--Update helper-->
-    <string name="update_download_started">Download of updates has started…</string>
-    <string name="update_download_success">An update has just been downloaded.</string>
-    <string name="update_download_success_button">Restart</string>
-    <string name="update_failed">Update failed! Wulkanowy may not function properly. Consider updating</string>
+    <string name="update_download_started">Завантаження оновлень розпочато…</string>
+    <string name="update_download_success">Щойно завантажено оновлення.</string>
+    <string name="update_download_success_button">Перезапустити</string>
+    <string name="update_failed">Помилка оновлення! Wulkanowy може не працювати належним чином. Подумайте про оновлення</string>
     <!--Errors-->
     <string name="error_no_internet">Брак з\'єднання з інтернетом</string>
-    <string name="error_timeout">Занадто довге очікування з\'єднання з щоденником</string>
-    <string name="error_login_failed">Login failed. Try again</string>
-    <string name="error_password_change_required">Потрібно змінити пароль</string>
+    <string name="error_timeout">Помилка підключення до реєстрації. Сервери можуть бути перевантажені. Будь-ласка спробуйте пізніше</string>
+    <string name="error_login_failed">Помилка завантаження даних. Будь-ласка спробуйте пізніше</string>
+    <string name="error_password_change_required">Потрібна реєстрація зміни пароля</string>
     <string name="error_service_unavailable">Технічна перерва в журналі UONET + продовжується. Спробуйте пізніше</string>
-    <string name="error_unknown_uonet">Unknown UONET + register error. Try again later</string>
-    <string name="error_unknown_app">Unknown application error</string>
+    <string name="error_unknown_uonet">Невідома помилка реєстру UONET+. Спробуйте ще раз пізніше</string>
+    <string name="error_unknown_app">Невідома помилка програми. Будь-ласка спробуйте пізніше</string>
     <string name="error_unknown">Відбулася несподівана помилка</string>
     <string name="error_feature_disabled">Функція вимкнена школою</string>
     <string name="error_feature_not_available">Функція не доступна в цьому режимі</string>

From a1ebf6c6ad806960504265f9e7095a3ec0c73a58 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?= <m.pich@outlook.com>
Date: Wed, 11 Nov 2020 16:03:52 +0100
Subject: [PATCH 12/34] Add average in class grades statistics (#1017)

---
 app/build.gradle                              |    2 +-
 .../29.json                                   | 1898 +++++++++++++++++
 .../GradeStatisticsLocalTest.kt               |   30 +-
 .../github/wulkanowy/utils/CrashLogUtils.kt   |    2 +-
 .../github/wulkanowy/data/RepositoryModule.kt |    8 +-
 .../github/wulkanowy/data/db/AppDatabase.kt   |   19 +-
 .../data/db/dao/GradePartialStatisticsDao.kt  |   13 +
 .../data/db/dao/GradeSemesterStatisticsDao.kt |   13 +
 .../data/db/dao/GradeStatisticsDao.kt         |   18 -
 .../db/entities/GradePartialStatistics.kt     |   33 +
 ...atistics.kt => GradeSemesterStatistics.kt} |   15 +-
 .../data/db/migrations/Migration29.kt         |   33 +
 .../data/pojos/GradeStatisticsItem.kt         |    9 +-
 .../gradestatistics/GradeStatisticsLocal.kt   |   52 +-
 .../gradestatistics/GradeStatisticsRemote.kt  |   49 +-
 .../GradeStatisticsRepository.kt              |  116 +-
 .../sync/works/GradeStatisticsWork.kt         |    4 +-
 .../statistics/GradeStatisticsAdapter.kt      |   58 +-
 .../statistics/GradeStatisticsPresenter.kt    |   15 +-
 .../GradeStatisticsRemoteTest.kt              |   28 +-
 20 files changed, 2270 insertions(+), 145 deletions(-)
 create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json
 create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/GradePartialStatisticsDao.kt
 create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSemesterStatisticsDao.kt
 delete mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt
 create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/entities/GradePartialStatistics.kt
 rename app/src/main/java/io/github/wulkanowy/data/db/entities/{GradeStatistics.kt => GradeSemesterStatistics.kt} (61%)
 create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt

diff --git a/app/build.gradle b/app/build.gradle
index ddb63d47..0afaab01 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -142,7 +142,7 @@ configurations.all {
 }
 
 dependencies {
-    implementation "io.github.wulkanowy:sdk:0.22.2"
+    implementation "io.github.wulkanowy:sdk:bcf6b53"
 
     coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
 
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json
new file mode 100644
index 00000000..3e863c57
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json
@@ -0,0 +1,1898 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 29,
+    "identityHash": "30e4647c7dd84a6ac9b2f9f3ef7d3264",
+    "entities": [
+      {
+        "tableName": "Students",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `scrapper_base_url` TEXT NOT NULL, `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "scrapperBaseUrl",
+            "columnName": "scrapper_base_url",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "mobileBaseUrl",
+            "columnName": "mobile_base_url",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "loginType",
+            "columnName": "login_type",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "loginMode",
+            "columnName": "login_mode",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "certificateKey",
+            "columnName": "certificate_key",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "privateKey",
+            "columnName": "private_key",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isParent",
+            "columnName": "is_parent",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "email",
+            "columnName": "email",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "password",
+            "columnName": "password",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "symbol",
+            "columnName": "symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "userLoginId",
+            "columnName": "user_login_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "userName",
+            "columnName": "user_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentName",
+            "columnName": "student_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "schoolSymbol",
+            "columnName": "school_id",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "schoolShortName",
+            "columnName": "school_short",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "schoolName",
+            "columnName": "school_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "className",
+            "columnName": "class_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "classId",
+            "columnName": "class_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isCurrent",
+            "columnName": "is_current",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "registrationDate",
+            "columnName": "registration_date",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [
+          {
+            "name": "index_Students_email_symbol_student_id_school_id_class_id",
+            "unique": true,
+            "columnNames": [
+              "email",
+              "symbol",
+              "student_id",
+              "school_id",
+              "class_id"
+            ],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)"
+          }
+        ],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Semesters",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "current",
+            "columnName": "is_current",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryName",
+            "columnName": "diary_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "schoolYear",
+            "columnName": "school_year",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterName",
+            "columnName": "semester_name",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "start",
+            "columnName": "start",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "end",
+            "columnName": "end",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "classId",
+            "columnName": "class_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "unitId",
+            "columnName": "unit_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [
+          {
+            "name": "index_Semesters_student_id_diary_id_semester_id",
+            "unique": true,
+            "columnNames": [
+              "student_id",
+              "diary_id",
+              "semester_id"
+            ],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `semester_id`)"
+          }
+        ],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Exams",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "entryDate",
+            "columnName": "entry_date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "group",
+            "columnName": "group",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "type",
+            "columnName": "type",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "description",
+            "columnName": "description",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacherSymbol",
+            "columnName": "teacher_symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Timetable",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "number",
+            "columnName": "number",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "start",
+            "columnName": "start",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "end",
+            "columnName": "end",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subjectOld",
+            "columnName": "subjectOld",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "group",
+            "columnName": "group",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "room",
+            "columnName": "room",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "roomOld",
+            "columnName": "roomOld",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacherOld",
+            "columnName": "teacherOld",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "info",
+            "columnName": "info",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isStudentPlan",
+            "columnName": "student_plan",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "changes",
+            "columnName": "changes",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "canceled",
+            "columnName": "canceled",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Attendance",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "timeId",
+            "columnName": "time_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "number",
+            "columnName": "number",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "presence",
+            "columnName": "presence",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "absence",
+            "columnName": "absence",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "exemption",
+            "columnName": "exemption",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lateness",
+            "columnName": "lateness",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "excused",
+            "columnName": "excused",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "deleted",
+            "columnName": "deleted",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "excusable",
+            "columnName": "excusable",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "excuseStatus",
+            "columnName": "excuse_status",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "AttendanceSummary",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subjectId",
+            "columnName": "subject_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "month",
+            "columnName": "month",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "presence",
+            "columnName": "presence",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "absence",
+            "columnName": "absence",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "absenceExcused",
+            "columnName": "absence_excused",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "absenceForSchoolReasons",
+            "columnName": "absence_for_school_reasons",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lateness",
+            "columnName": "lateness",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "latenessExcused",
+            "columnName": "lateness_excused",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "exemption",
+            "columnName": "exemption",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Grades",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isRead",
+            "columnName": "is_read",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isNotified",
+            "columnName": "is_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "entry",
+            "columnName": "entry",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "value",
+            "columnName": "value",
+            "affinity": "REAL",
+            "notNull": true
+          },
+          {
+            "fieldPath": "modifier",
+            "columnName": "modifier",
+            "affinity": "REAL",
+            "notNull": true
+          },
+          {
+            "fieldPath": "comment",
+            "columnName": "comment",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "color",
+            "columnName": "color",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "gradeSymbol",
+            "columnName": "grade_symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "description",
+            "columnName": "description",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "weight",
+            "columnName": "weight",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "weightValue",
+            "columnName": "weightValue",
+            "affinity": "REAL",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "GradesSummary",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isPredictedGradeNotified",
+            "columnName": "is_predicted_grade_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isFinalGradeNotified",
+            "columnName": "is_final_grade_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "predictedGradeLastChange",
+            "columnName": "predicted_grade_last_change",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "finalGradeLastChange",
+            "columnName": "final_grade_last_change",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "position",
+            "columnName": "position",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "predictedGrade",
+            "columnName": "predicted_grade",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "finalGrade",
+            "columnName": "final_grade",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "proposedPoints",
+            "columnName": "proposed_points",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "finalPoints",
+            "columnName": "final_points",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "pointsSum",
+            "columnName": "points_sum",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "average",
+            "columnName": "average",
+            "affinity": "REAL",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "GradePartialStatistics",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "classAverage",
+            "columnName": "class_average",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentAverage",
+            "columnName": "student_average",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "classAmounts",
+            "columnName": "class_amounts",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentAmounts",
+            "columnName": "student_amounts",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "GradesPointsStatistics",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "others",
+            "columnName": "others",
+            "affinity": "REAL",
+            "notNull": true
+          },
+          {
+            "fieldPath": "student",
+            "columnName": "student",
+            "affinity": "REAL",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "GradeSemesterStatistics",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "amounts",
+            "columnName": "amounts",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentGrade",
+            "columnName": "student_grade",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Messages",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `unread_by` INTEGER NOT NULL, `read_by` INTEGER NOT NULL, `content` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `recipient_name` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `removed` INTEGER NOT NULL, `has_attachments` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isNotified",
+            "columnName": "is_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "unreadBy",
+            "columnName": "unread_by",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "readBy",
+            "columnName": "read_by",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "content",
+            "columnName": "content",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "realId",
+            "columnName": "real_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "messageId",
+            "columnName": "message_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "sender",
+            "columnName": "sender_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "senderId",
+            "columnName": "sender_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "recipient",
+            "columnName": "recipient_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "folderId",
+            "columnName": "folder_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "unread",
+            "columnName": "unread",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "removed",
+            "columnName": "removed",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "hasAttachments",
+            "columnName": "has_attachments",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "MessageAttachments",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`real_id` INTEGER NOT NULL, `message_id` INTEGER NOT NULL, `one_drive_id` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`real_id`))",
+        "fields": [
+          {
+            "fieldPath": "realId",
+            "columnName": "real_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "messageId",
+            "columnName": "message_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "oneDriveId",
+            "columnName": "one_drive_id",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "url",
+            "columnName": "url",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "filename",
+            "columnName": "filename",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "real_id"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Notes",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isRead",
+            "columnName": "is_read",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isNotified",
+            "columnName": "is_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacherSymbol",
+            "columnName": "teacher_symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "category",
+            "columnName": "category",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "categoryType",
+            "columnName": "category_type",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isPointsShow",
+            "columnName": "is_points_show",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "points",
+            "columnName": "points",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "content",
+            "columnName": "content",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Homework",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isDone",
+            "columnName": "is_done",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "semesterId",
+            "columnName": "semester_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "entryDate",
+            "columnName": "entry_date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "content",
+            "columnName": "content",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacherSymbol",
+            "columnName": "teacher_symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "attachments",
+            "columnName": "attachments",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Subjects",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "realId",
+            "columnName": "real_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "LuckyNumbers",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "isNotified",
+            "columnName": "is_notified",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "luckyNumber",
+            "columnName": "lucky_number",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "CompletedLesson",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "number",
+            "columnName": "number",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "topic",
+            "columnName": "topic",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacher",
+            "columnName": "teacher",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "teacherSymbol",
+            "columnName": "teacher_symbol",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "substitution",
+            "columnName": "substitution",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "absence",
+            "columnName": "absence",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "resources",
+            "columnName": "resources",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "ReportingUnits",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `short` TEXT NOT NULL, `sender_id` INTEGER NOT NULL, `sender_name` TEXT NOT NULL, `roles` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "realId",
+            "columnName": "real_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "shortName",
+            "columnName": "short",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "senderId",
+            "columnName": "sender_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "senderName",
+            "columnName": "sender_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "roles",
+            "columnName": "roles",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Recipients",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `real_id` TEXT NOT NULL, `name` TEXT NOT NULL, `real_name` TEXT NOT NULL, `login_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `role` INTEGER NOT NULL, `hash` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "realId",
+            "columnName": "real_id",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "realName",
+            "columnName": "real_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "loginId",
+            "columnName": "login_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "unitId",
+            "columnName": "unit_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "role",
+            "columnName": "role",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "hash",
+            "columnName": "hash",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "MobileDevices",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "deviceId",
+            "columnName": "device_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Teachers",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "classId",
+            "columnName": "class_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "shortName",
+            "columnName": "short_name",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "School",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "classId",
+            "columnName": "class_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "address",
+            "columnName": "address",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "contact",
+            "columnName": "contact",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "headmaster",
+            "columnName": "headmaster",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "pedagogue",
+            "columnName": "pedagogue",
+            "affinity": "TEXT",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Conferences",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "studentId",
+            "columnName": "student_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "diaryId",
+            "columnName": "diary_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "title",
+            "columnName": "title",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "subject",
+            "columnName": "subject",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "agenda",
+            "columnName": "agenda",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "presentOnConference",
+            "columnName": "present_on_conference",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "conferenceId",
+            "columnName": "conference_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "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, '30e4647c7dd84a6ac9b2f9f3ef7d3264')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt
index 197d2d0e..db083432 100644
--- a/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt
+++ b/app/src/androidTest/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocalTest.kt
@@ -4,8 +4,8 @@ import androidx.room.Room
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import io.github.wulkanowy.data.db.AppDatabase
+import io.github.wulkanowy.data.db.entities.GradePartialStatistics
 import io.github.wulkanowy.data.db.entities.GradePointsStatistics
-import io.github.wulkanowy.data.db.entities.GradeStatistics
 import io.github.wulkanowy.data.db.entities.Semester
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.runBlocking
@@ -27,7 +27,7 @@ class GradeStatisticsLocalTest {
     fun createDb() {
         testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
             .build()
-        gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradeStatistics, testDb.gradePointsStatistics)
+        gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradePartialStatisticsDao, testDb.gradePointsStatisticsDao, testDb.gradeSemesterStatisticsDao)
     }
 
     @After
@@ -41,9 +41,9 @@ class GradeStatisticsLocalTest {
             getGradeStatistics("Matematyka", 2, 1),
             getGradeStatistics("Fizyka", 1, 2)
         )
-        runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) }
+        runBlocking { gradeStatisticsLocal.saveGradePartialStatistics(list) }
 
-        val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() }
+        val stats = runBlocking { gradeStatisticsLocal.getGradePartialStatistics(getSemester()).first() }
         assertEquals(1, stats.size)
         assertEquals(stats[0].subject, "Matematyka")
     }
@@ -55,12 +55,10 @@ class GradeStatisticsLocalTest {
             getGradeStatistics("Chemia", 2, 1),
             getGradeStatistics("Fizyka", 1, 2)
         )
-        runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) }
+        runBlocking { gradeStatisticsLocal.saveGradePartialStatistics(list) }
 
-        val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() }
+        val stats = runBlocking { gradeStatisticsLocal.getGradePartialStatistics(getSemester()).first() }
         assertEquals(2, stats.size)
-//        assertEquals(3, stats.size)
-//        assertEquals(stats[0].subject, "Wszystkie") // now in main repo
         assertEquals(stats[0].subject, "Matematyka")
         assertEquals(stats[1].subject, "Chemia")
     }
@@ -72,9 +70,9 @@ class GradeStatisticsLocalTest {
             getGradePointsStatistics("Chemia", 2, 1),
             getGradePointsStatistics("Fizyka", 1, 2)
         )
-        runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(list) }
+        runBlocking { gradeStatisticsLocal.saveGradePointsStatistics(list) }
 
-        val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
+        val stats = runBlocking { gradeStatisticsLocal.getGradePointsStatistics(getSemester()).first() }
         with(stats[0]) {
             assertEquals(subject, "Matematyka")
             assertEquals(others, 5.0)
@@ -84,17 +82,17 @@ class GradeStatisticsLocalTest {
 
     @Test
     fun saveAndRead_subjectEmpty() {
-        runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) }
+        runBlocking { gradeStatisticsLocal.saveGradePointsStatistics(listOf()) }
 
-        val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
+        val stats = runBlocking { gradeStatisticsLocal.getGradePointsStatistics(getSemester()).first() }
         assertEquals(emptyList(), stats)
     }
 
     @Test
     fun saveAndRead_allEmpty() {
-        runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) }
+        runBlocking { gradeStatisticsLocal.saveGradePointsStatistics(listOf()) }
 
-        val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
+        val stats = runBlocking { gradeStatisticsLocal.getGradePointsStatistics(getSemester()).first() }
         assertEquals(emptyList(), stats)
     }
 
@@ -102,8 +100,8 @@ class GradeStatisticsLocalTest {
         return Semester(2, 2, "", 2019, 1, 2, now(), now(), 1, 1)
     }
 
-    private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics {
-        return GradeStatistics(studentId, semesterId, subject, 5, 5, false)
+    private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradePartialStatistics {
+        return GradePartialStatistics(studentId, semesterId, subject, "", "", listOf(5), listOf(5))
     }
 
     private fun getGradePointsStatistics(subject: String, studentId: Int, semesterId: Int): GradePointsStatistics {
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 7f4bedae..8adf2b29 100644
--- a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt
+++ b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt
@@ -42,7 +42,7 @@ class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR) {
         connectCrash.setCustomKey("priority", priority)
         connectCrash.setCustomKey("tag", tag.orEmpty())
         connectCrash.setCustomKey("message", message)
-        connectCrash.log(priority, t?.stackTraceToString())
+
         if (t != null) {
             connectCrash.log(priority, t.stackTraceToString())
         } else {
diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
index 50642ddd..5efe7773 100644
--- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
@@ -85,11 +85,15 @@ internal class RepositoryModule {
 
     @Singleton
     @Provides
-    fun provideGradeStatisticsDao(database: AppDatabase) = database.gradeStatistics
+    fun provideGradePartialStatisticsDao(database: AppDatabase) = database.gradePartialStatisticsDao
 
     @Singleton
     @Provides
-    fun provideGradePointsStatisticsDao(database: AppDatabase) = database.gradePointsStatistics
+    fun provideGradeSemesterStatisticsDao(database: AppDatabase) = database.gradeSemesterStatisticsDao
+
+    @Singleton
+    @Provides
+    fun provideGradePointsStatisticsDao(database: AppDatabase) = database.gradePointsStatisticsDao
 
     @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 93f22542..57160a2b 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
@@ -13,8 +13,9 @@ 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.GradeStatisticsDao
+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
@@ -36,8 +37,9 @@ 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.GradeStatistics
+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
@@ -73,6 +75,7 @@ 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.Migration4
 import io.github.wulkanowy.data.db.migrations.Migration5
@@ -93,8 +96,9 @@ import javax.inject.Singleton
         AttendanceSummary::class,
         Grade::class,
         GradeSummary::class,
-        GradeStatistics::class,
+        GradePartialStatistics::class,
         GradePointsStatistics::class,
+        GradeSemesterStatistics::class,
         Message::class,
         MessageAttachment::class,
         Note::class,
@@ -116,7 +120,7 @@ import javax.inject.Singleton
 abstract class AppDatabase : RoomDatabase() {
 
     companion object {
-        const val VERSION_SCHEMA = 28
+        const val VERSION_SCHEMA = 29
 
         fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
             return arrayOf(
@@ -147,6 +151,7 @@ abstract class AppDatabase : RoomDatabase() {
                 Migration26(),
                 Migration27(),
                 Migration28(),
+                Migration29()
             )
         }
 
@@ -176,9 +181,11 @@ abstract class AppDatabase : RoomDatabase() {
 
     abstract val gradeSummaryDao: GradeSummaryDao
 
-    abstract val gradeStatistics: GradeStatisticsDao
+    abstract val gradePartialStatisticsDao: GradePartialStatisticsDao
 
-    abstract val gradePointsStatistics: GradePointsStatisticsDao
+    abstract val gradePointsStatisticsDao: GradePointsStatisticsDao
+
+    abstract val gradeSemesterStatisticsDao: GradeSemesterStatisticsDao
 
     abstract val messagesDao: MessagesDao
 
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePartialStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePartialStatisticsDao.kt
new file mode 100644
index 00000000..bce6ce57
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradePartialStatisticsDao.kt
@@ -0,0 +1,13 @@
+package io.github.wulkanowy.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import io.github.wulkanowy.data.db.entities.GradePartialStatistics
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface GradePartialStatisticsDao : BaseDao<GradePartialStatistics> {
+
+    @Query("SELECT * FROM GradePartialStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
+    fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradePartialStatistics>>
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSemesterStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSemesterStatisticsDao.kt
new file mode 100644
index 00000000..09ae8171
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeSemesterStatisticsDao.kt
@@ -0,0 +1,13 @@
+package io.github.wulkanowy.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface GradeSemesterStatisticsDao : BaseDao<GradeSemesterStatistics> {
+
+    @Query("SELECT * FROM GradeSemesterStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
+    fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradeSemesterStatistics>>
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt
deleted file mode 100644
index b462ad5d..00000000
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/GradeStatisticsDao.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.github.wulkanowy.data.db.dao
-
-import androidx.room.Dao
-import androidx.room.Query
-import io.github.wulkanowy.data.db.entities.GradeStatistics
-import kotlinx.coroutines.flow.Flow
-import javax.inject.Singleton
-
-@Singleton
-@Dao
-interface GradeStatisticsDao : BaseDao<GradeStatistics> {
-
-    @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester")
-    fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Flow<List<GradeStatistics>>
-
-    @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND is_semester = :isSemester")
-    fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Flow<List<GradeStatistics>>
-}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePartialStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePartialStatistics.kt
new file mode 100644
index 00000000..db164afd
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradePartialStatistics.kt
@@ -0,0 +1,33 @@
+package io.github.wulkanowy.data.db.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity(tableName = "GradePartialStatistics")
+data class GradePartialStatistics(
+
+    @ColumnInfo(name = "student_id")
+    val studentId: Int,
+
+    @ColumnInfo(name = "semester_id")
+    val semesterId: Int,
+
+    val subject: String,
+
+    @ColumnInfo(name = "class_average")
+    val classAverage: String,
+
+    @ColumnInfo(name = "student_average")
+    val studentAverage: String,
+
+    @ColumnInfo(name = "class_amounts")
+    val classAmounts: List<Int>,
+
+    @ColumnInfo(name = "student_amounts")
+    val studentAmounts: List<Int>
+
+) {
+    @PrimaryKey(autoGenerate = true)
+    var id: Long = 0
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt
similarity index 61%
rename from app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt
rename to app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt
index 8ad8b8b8..e747271c 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeStatistics.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/GradeSemesterStatistics.kt
@@ -4,8 +4,8 @@ import androidx.room.ColumnInfo
 import androidx.room.Entity
 import androidx.room.PrimaryKey
 
-@Entity(tableName = "GradesStatistics")
-data class GradeStatistics(
+@Entity(tableName = "GradeSemesterStatistics")
+data class GradeSemesterStatistics(
 
     @ColumnInfo(name = "student_id")
     val studentId: Int,
@@ -15,13 +15,14 @@ data class GradeStatistics(
 
     val subject: String,
 
-    val grade: Int,
+    val amounts: List<Int>,
 
-    val amount: Int,
-
-    @ColumnInfo(name = "is_semester")
-    val semester: Boolean
+    @ColumnInfo(name = "student_grade")
+    val studentGrade: Int
 ) {
     @PrimaryKey(autoGenerate = true)
     var id: Long = 0
+
+    @Transient
+    var average: String = ""
 }
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt
new file mode 100644
index 00000000..327552d7
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/migrations/Migration29.kt
@@ -0,0 +1,33 @@
+package io.github.wulkanowy.data.db.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+class Migration29 : Migration(28, 29) {
+
+    override fun migrate(database: SupportSQLiteDatabase) {
+        database.execSQL("DROP TABLE IF EXISTS GradesStatistics")
+        database.execSQL("""
+            CREATE TABLE IF NOT EXISTS GradeSemesterStatistics (
+                id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+                student_id INTEGER NOT NULL,
+                semester_id INTEGER NOT NULL,
+                subject TEXT NOT NULL,
+                amounts TEXT NOT NULL,
+                student_grade INTEGER NOT NULL
+            )
+        """)
+        database.execSQL("""
+            CREATE TABLE IF NOT EXISTS GradePartialStatistics (
+                id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+                student_id INTEGER NOT NULL,
+                semester_id INTEGER NOT NULL,
+                subject TEXT NOT NULL,
+                class_average TEXT NOT NULL,
+                student_average TEXT NOT NULL,
+                class_amounts TEXT NOT NULL,
+                student_amounts TEXT NOT NULL
+            )
+        """)
+    }
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt
index 34b62abd..88257470 100644
--- a/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/pojos/GradeStatisticsItem.kt
@@ -1,14 +1,19 @@
 package io.github.wulkanowy.data.pojos
 
+import io.github.wulkanowy.data.db.entities.GradePartialStatistics
 import io.github.wulkanowy.data.db.entities.GradePointsStatistics
-import io.github.wulkanowy.data.db.entities.GradeStatistics
+import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
 import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
 
 data class GradeStatisticsItem(
 
     val type: ViewType,
 
-    val partial: List<GradeStatistics>,
+    val average: String,
+
+    val partial: GradePartialStatistics?,
+
+    val semester: GradeSemesterStatistics?,
 
     val points: GradePointsStatistics?
 )
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt
index e0e2cd4d..3e3c819f 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsLocal.kt
@@ -1,9 +1,11 @@
 package io.github.wulkanowy.data.repositories.gradestatistics
 
+import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
 import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
-import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
+import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
+import io.github.wulkanowy.data.db.entities.GradePartialStatistics
 import io.github.wulkanowy.data.db.entities.GradePointsStatistics
-import io.github.wulkanowy.data.db.entities.GradeStatistics
+import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
 import io.github.wulkanowy.data.db.entities.Semester
 import kotlinx.coroutines.flow.Flow
 import javax.inject.Inject
@@ -11,31 +13,47 @@ import javax.inject.Singleton
 
 @Singleton
 class GradeStatisticsLocal @Inject constructor(
-    private val gradeStatisticsDb: GradeStatisticsDao,
-    private val gradePointsStatisticsDb: GradePointsStatisticsDao
+    private val gradePartialStatisticsDb: GradePartialStatisticsDao,
+    private val gradePointsStatisticsDb: GradePointsStatisticsDao,
+    private val gradeSemesterStatisticsDb: GradeSemesterStatisticsDao
 ) {
 
-    fun getGradesStatistics(semester: Semester, isSemester: Boolean): Flow<List<GradeStatistics>> {
-        return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester)
+    // partial
+    fun getGradePartialStatistics(semester: Semester): Flow<List<GradePartialStatistics>> {
+        return gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId)
     }
 
-    fun getGradesPointsStatistics(semester: Semester): Flow<List<GradePointsStatistics>> {
+    suspend fun saveGradePartialStatistics(items: List<GradePartialStatistics>) {
+        gradePartialStatisticsDb.insertAll(items)
+    }
+
+    suspend fun deleteGradePartialStatistics(items: List<GradePartialStatistics>) {
+        gradePartialStatisticsDb.deleteAll(items)
+    }
+
+    // points
+    fun getGradePointsStatistics(semester: Semester): Flow<List<GradePointsStatistics>> {
         return gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId)
     }
 
-    suspend fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) {
-        gradeStatisticsDb.insertAll(gradesStatistics)
-    }
-
-    suspend fun saveGradesPointsStatistics(gradePointsStatistics: List<GradePointsStatistics>) {
+    suspend fun saveGradePointsStatistics(gradePointsStatistics: List<GradePointsStatistics>) {
         gradePointsStatisticsDb.insertAll(gradePointsStatistics)
     }
 
-    suspend fun deleteGradesStatistics(gradesStatistics: List<GradeStatistics>) {
-        gradeStatisticsDb.deleteAll(gradesStatistics)
-    }
-
-    suspend fun deleteGradesPointsStatistics(gradesPointsStatistics: List<GradePointsStatistics>) {
+    suspend fun deleteGradePointsStatistics(gradesPointsStatistics: List<GradePointsStatistics>) {
         gradePointsStatisticsDb.deleteAll(gradesPointsStatistics)
     }
+
+    // semester
+    fun getGradeSemesterStatistics(semester: Semester): Flow<List<GradeSemesterStatistics>> {
+        return gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId)
+    }
+
+    suspend fun saveGradeSemesterStatistics(items: List<GradeSemesterStatistics>) {
+        gradeSemesterStatisticsDb.insertAll(items)
+    }
+
+    suspend fun deleteGradeSemesterStatistics(items: List<GradeSemesterStatistics>) {
+        gradeSemesterStatisticsDb.deleteAll(items)
+    }
 }
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt
index 1ff8132f..144df8a0 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemote.kt
@@ -1,7 +1,8 @@
 package io.github.wulkanowy.data.repositories.gradestatistics
 
+import io.github.wulkanowy.data.db.entities.GradePartialStatistics
 import io.github.wulkanowy.data.db.entities.GradePointsStatistics
-import io.github.wulkanowy.data.db.entities.GradeStatistics
+import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
 import io.github.wulkanowy.data.db.entities.Semester
 import io.github.wulkanowy.data.db.entities.Student
 import io.github.wulkanowy.sdk.Sdk
@@ -12,20 +13,38 @@ import javax.inject.Singleton
 @Singleton
 class GradeStatisticsRemote @Inject constructor(private val sdk: Sdk) {
 
-    suspend fun getGradeStatistics(student: Student, semester: Semester, isSemester: Boolean): List<GradeStatistics> {
-        return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).let {
-            if (isSemester) it.getGradesAnnualStatistics(semester.semesterId)
-            else it.getGradesPartialStatistics(semester.semesterId)
-        }.map {
-            GradeStatistics(
-                semesterId = semester.semesterId,
-                studentId = semester.studentId,
-                subject = it.subject,
-                grade = it.gradeValue,
-                amount = it.amount,
-                semester = isSemester
-            )
-        }
+    suspend fun getGradePartialStatistics(student: Student, semester: Semester): List<GradePartialStatistics> {
+        return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+            .getGradesPartialStatistics(semester.semesterId)
+            .map {
+                GradePartialStatistics(
+                    semesterId = semester.semesterId,
+                    studentId = student.studentId,
+                    subject = it.subject,
+                    classAverage = it.classAverage,
+                    studentAverage = it.studentAverage,
+                    classAmounts = it.classItems
+                        .sortedBy { item -> item.grade }
+                        .map { item -> item.amount },
+                    studentAmounts = it.studentItems.map { item -> item.amount }
+                )
+            }
+    }
+
+    suspend fun getGradeSemesterStatistics(student: Student, semester: Semester): List<GradeSemesterStatistics> {
+        return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
+            .getGradesSemesterStatistics(semester.semesterId)
+            .map {
+                GradeSemesterStatistics(
+                    semesterId = semester.semesterId,
+                    studentId = semester.studentId,
+                    subject = it.subject,
+                    amounts = it.items
+                        .sortedBy { item -> item.grade }
+                        .map { item -> item.amount },
+                    studentGrade = it.items.singleOrNull { item -> item.isStudentHere }?.grade ?: 0
+                )
+            }
     }
 
     suspend fun getGradePointsStatistics(student: Student, semester: Semester): List<GradePointsStatistics> {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt
index 52ca705f..ca0f6ffb 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRepository.kt
@@ -1,13 +1,15 @@
 package io.github.wulkanowy.data.repositories.gradestatistics
 
+import io.github.wulkanowy.data.db.entities.GradePartialStatistics
 import io.github.wulkanowy.data.db.entities.GradePointsStatistics
-import io.github.wulkanowy.data.db.entities.GradeStatistics
+import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
 import io.github.wulkanowy.data.db.entities.Semester
 import io.github.wulkanowy.data.db.entities.Student
 import io.github.wulkanowy.data.pojos.GradeStatisticsItem
 import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
 import io.github.wulkanowy.utils.networkBoundResource
 import io.github.wulkanowy.utils.uniqueSubtract
+import java.util.Locale
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -17,55 +19,125 @@ class GradeStatisticsRepository @Inject constructor(
     private val remote: GradeStatisticsRemote
 ) {
 
-    fun getGradesStatistics(student: Student, semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean) = networkBoundResource(
+    fun getGradesPartialStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
         shouldFetch = { it.isEmpty() || forceRefresh },
-        query = { local.getGradesStatistics(semester, isSemester) },
-        fetch = { remote.getGradeStatistics(student, semester, isSemester) },
+        query = { local.getGradePartialStatistics(semester) },
+        fetch = { remote.getGradePartialStatistics(student, semester) },
         saveFetchResult = { old, new ->
-            local.deleteGradesStatistics(old uniqueSubtract new)
-            local.saveGradesStatistics(new uniqueSubtract old)
+            local.deleteGradePartialStatistics(old uniqueSubtract new)
+            local.saveGradePartialStatistics(new uniqueSubtract old)
         },
         mapResult = { items ->
             when (subjectName) {
-                "Wszystkie" -> items.groupBy { it.grade }.map {
-                    GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key,
-                        it.value.fold(0) { acc, e -> acc + e.amount }, false)
-                } + items
+                "Wszystkie" -> {
+                    val numerator = items.map {
+                        it.classAverage.replace(",", ".").toDoubleOrNull() ?: .0
+                    }.filterNot { it == .0 }
+                    (items.reversed() + GradePartialStatistics(
+                        studentId = semester.studentId,
+                        semesterId = semester.semesterId,
+                        subject = subjectName,
+                        classAverage = if (numerator.isEmpty()) "" else numerator.average().let {
+                            "%.2f".format(Locale.FRANCE, it)
+                        },
+                        studentAverage = "",
+                        classAmounts = items.map { it.classAmounts }.sumGradeAmounts(),
+                        studentAmounts = items.map { it.studentAmounts }.sumGradeAmounts()
+                    )).reversed()
+                }
                 else -> items.filter { it.subject == subjectName }
-            }.mapToStatisticItems()
+            }.mapPartialToStatisticItems()
+        }
+    )
+
+    fun getGradesSemesterStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
+        shouldFetch = { it.isEmpty() || forceRefresh },
+        query = { local.getGradeSemesterStatistics(semester) },
+        fetch = { remote.getGradeSemesterStatistics(student, semester) },
+        saveFetchResult = { old, new ->
+            local.deleteGradeSemesterStatistics(old uniqueSubtract new)
+            local.saveGradeSemesterStatistics(new uniqueSubtract old)
+        },
+        mapResult = { items ->
+            val itemsWithAverage = items.map { item ->
+                item.copy().apply {
+                    val denominator = item.amounts.sum()
+                    average = if (denominator == 0) "" else (item.amounts.mapIndexed { gradeValue, amount ->
+                        (gradeValue + 1) * amount
+                    }.sum().toDouble() / denominator).let {
+                        "%.2f".format(Locale.FRANCE, it)
+                    }
+                }
+            }
+            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)
+                    }
+                }).reversed()
+                else -> itemsWithAverage.filter { it.subject == subjectName }
+            }.mapSemesterToStatisticItems()
         }
     )
 
     fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
         shouldFetch = { it.isEmpty() || forceRefresh },
-        query = { local.getGradesPointsStatistics(semester) },
+        query = { local.getGradePointsStatistics(semester) },
         fetch = { remote.getGradePointsStatistics(student, semester) },
         saveFetchResult = { old, new ->
-            local.deleteGradesPointsStatistics(old uniqueSubtract new)
-            local.saveGradesPointsStatistics(new uniqueSubtract old)
+            local.deleteGradePointsStatistics(old uniqueSubtract new)
+            local.saveGradePointsStatistics(new uniqueSubtract old)
         },
         mapResult = { items ->
             when (subjectName) {
                 "Wszystkie" -> items
                 else -> items.filter { it.subject == subjectName }
-            }.mapToStatisticsItem()
+            }.mapPointsToStatisticsItems()
         }
     )
 
-    private fun List<GradeStatistics>.mapToStatisticItems() = groupBy { it.subject }.map {
+    private fun List<List<Int>>.sumGradeAmounts(): List<Int> {
+        val result = mutableListOf(0, 0, 0, 0, 0, 0)
+        forEach {
+            it.forEachIndexed { grade, amount ->
+                result[grade] += amount
+            }
+        }
+        return result
+    }
+
+    private fun List<GradePartialStatistics>.mapPartialToStatisticItems() = filterNot { it.classAmounts.isEmpty() }.map {
         GradeStatisticsItem(
             type = ViewType.PARTIAL,
-            partial = it.value
-                .sortedByDescending { item -> item.grade }
-                .filter { item -> item.amount != 0 },
-            points = null
+            average = it.classAverage,
+            partial = it,
+            points = null,
+            semester = null
         )
     }
 
-    private fun List<GradePointsStatistics>.mapToStatisticsItem() = map {
+    private fun List<GradeSemesterStatistics>.mapSemesterToStatisticItems() = filterNot { it.amounts.isEmpty() }.map {
+        GradeStatisticsItem(
+            type = ViewType.SEMESTER,
+            partial = null,
+            points = null,
+            average = "",
+            semester = it
+        )
+    }
+
+    private fun List<GradePointsStatistics>.mapPointsToStatisticsItems() = map {
         GradeStatisticsItem(
             type = ViewType.POINTS,
-            partial = emptyList(),
+            partial = null,
+            semester = null,
+            average = "",
             points = it
         )
     }
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 3d61a423..461836b8 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
@@ -12,8 +12,8 @@ class GradeStatisticsWork @Inject constructor(
 
     override suspend fun doWork(student: Student, semester: Semester) {
         with(gradeStatisticsRepository) {
-            getGradesStatistics(student, semester, "Wszystkie", isSemester = true, forceRefresh = true).waitForResult()
-            getGradesStatistics(student, semester, "Wszystkie", isSemester = false, forceRefresh = true).waitForResult()
+            getGradesPartialStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()
+            getGradesSemesterStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()
             getGradesPointsStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()
         }
     }
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 dbb60910..cbcb444a 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
@@ -17,8 +17,9 @@ import com.github.mikephil.charting.data.PieDataSet
 import com.github.mikephil.charting.data.PieEntry
 import com.github.mikephil.charting.formatter.ValueFormatter
 import io.github.wulkanowy.R
+import io.github.wulkanowy.data.db.entities.GradePartialStatistics
 import io.github.wulkanowy.data.db.entities.GradePointsStatistics
-import io.github.wulkanowy.data.db.entities.GradeStatistics
+import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
 import io.github.wulkanowy.data.pojos.GradeStatisticsItem
 import io.github.wulkanowy.databinding.ItemGradeStatisticsBarBinding
 import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding
@@ -68,22 +69,32 @@ class GradeStatisticsAdapter @Inject constructor() :
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
         val inflater = LayoutInflater.from(parent.context)
         return when (viewType) {
-            ViewType.PARTIAL.id, ViewType.SEMESTER.id -> PieViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false))
-            ViewType.POINTS.id -> BarViewHolder(ItemGradeStatisticsBarBinding.inflate(inflater, parent, false))
+            ViewType.PARTIAL.id -> PartialViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false))
+            ViewType.SEMESTER.id -> SemesterViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false))
+            ViewType.POINTS.id -> PointsViewHolder(ItemGradeStatisticsBarBinding.inflate(inflater, parent, false))
             else -> throw IllegalStateException()
         }
     }
 
     override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
         when (holder) {
-            is PieViewHolder -> bindPieChart(holder, items[position].partial)
-            is BarViewHolder -> bindBarChart(holder, items[position].points!!)
+            is PartialViewHolder -> bindPartialChart(holder, items[position].partial!!)
+            is SemesterViewHolder -> bindSemesterChart(holder, items[position].semester!!)
+            is PointsViewHolder -> bindBarChart(holder, items[position].points!!)
         }
     }
 
-    private fun bindPieChart(holder: PieViewHolder, partials: List<GradeStatistics>) {
-        with(holder.binding.gradeStatisticsPieTitle) {
-            text = partials.firstOrNull()?.subject
+    private fun bindPartialChart(holder: PartialViewHolder, partials: GradePartialStatistics) {
+        bindPieChart(holder.binding, partials.subject, partials.classAverage, partials.classAmounts)
+    }
+
+    private fun bindSemesterChart(holder: SemesterViewHolder, semester: GradeSemesterStatistics) {
+        bindPieChart(holder.binding, semester.subject, semester.average, semester.amounts)
+    }
+
+    private fun bindPieChart(binding: ItemGradeStatisticsPieBinding, subject: String, average: String, amounts: List<Int>) {
+        with(binding.gradeStatisticsPieTitle) {
+            text = subject
             visibility = if (items.size == 1 || !showAllSubjectsOnList) GONE else VISIBLE
         }
 
@@ -92,22 +103,23 @@ class GradeStatisticsAdapter @Inject constructor() :
             else -> materialGradeColors
         }
 
-        val dataset = PieDataSet(partials.map {
-            PieEntry(it.amount.toFloat(), it.grade.toString())
-        }, "Legenda")
+        val dataset = PieDataSet(amounts.mapIndexed { grade, amount ->
+            PieEntry(amount.toFloat(), (grade + 1).toString())
+        }.reversed().filterNot { it.value == 0f }, "Legenda")
 
         with(dataset) {
             valueTextSize = 12f
             sliceSpace = 1f
             valueTextColor = Color.WHITE
-            setColors(partials.map {
-                gradeColors.single { color -> color.first == it.grade }.second
-            }.toIntArray(), holder.binding.root.context)
+            val grades = amounts.mapIndexed { grade, amount -> (grade + 1) to amount }.filterNot { it.second == 0 }
+            setColors(grades.reversed().map { (grade, _) ->
+                gradeColors.single { color -> color.first == grade }.second
+            }.toIntArray(), binding.root.context)
         }
 
-        with(holder.binding.gradeStatisticsPie) {
+        with(binding.gradeStatisticsPie) {
             setTouchEnabled(false)
-            if (partials.size == 1) animateXY(1000, 1000)
+            if (amounts.size == 1) animateXY(1000, 1000)
             data = PieData(dataset).apply {
                 setValueFormatter(object : ValueFormatter() {
                     override fun getPieLabel(value: Float, pieEntry: PieEntry): String {
@@ -128,8 +140,9 @@ class GradeStatisticsAdapter @Inject constructor() :
 
             minAngleForSlices = 25f
             description.isEnabled = false
-            centerText = partials.fold(0) { acc, it -> acc + it.amount }
-                .let { resources.getQuantityString(R.plurals.grade_number_item, it, it) }
+            centerText = amounts.fold(0) { acc, it -> acc + it }
+                .let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } +
+                ("\n\nŚrednia: $average").takeIf { average.isNotBlank() }.orEmpty()
 
             setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
             setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
@@ -137,7 +150,7 @@ class GradeStatisticsAdapter @Inject constructor() :
         }
     }
 
-    private fun bindBarChart(holder: BarViewHolder, points: GradePointsStatistics) {
+    private fun bindBarChart(holder: PointsViewHolder, points: GradePointsStatistics) {
         with(holder.binding.gradeStatisticsBarTitle) {
             text = points.subject
             visibility = if (items.size == 1) GONE else VISIBLE
@@ -200,9 +213,12 @@ class GradeStatisticsAdapter @Inject constructor() :
         }
     }
 
-    private class PieViewHolder(val binding: ItemGradeStatisticsPieBinding) :
+    private class PartialViewHolder(val binding: ItemGradeStatisticsPieBinding) :
         RecyclerView.ViewHolder(binding.root)
 
-    private class BarViewHolder(val binding: ItemGradeStatisticsBarBinding) :
+    private class SemesterViewHolder(val binding: ItemGradeStatisticsPieBinding) :
+        RecyclerView.ViewHolder(binding.root)
+
+    private class PointsViewHolder(val binding: ItemGradeStatisticsBarBinding) :
         RecyclerView.ViewHolder(binding.root)
 }
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 2f46d21d..aca3ec96 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
@@ -153,8 +153,8 @@ class GradeStatisticsPresenter @Inject constructor(
 
             with(gradeStatisticsRepository) {
                 when (type) {
-                    ViewType.SEMESTER -> getGradesStatistics(student, semester, currentSubjectName, true, forceRefresh)
-                    ViewType.PARTIAL -> getGradesStatistics(student, semester, currentSubjectName, false, forceRefresh)
+                    ViewType.PARTIAL -> getGradesPartialStatistics(student, semester, currentSubjectName, forceRefresh)
+                    ViewType.SEMESTER -> getGradesSemesterStatistics(student, semester, currentSubjectName, forceRefresh)
                     ViewType.POINTS -> getGradesPointsStatistics(student, semester, currentSubjectName, forceRefresh)
                 }
             }
@@ -164,8 +164,15 @@ class GradeStatisticsPresenter @Inject constructor(
                 Status.SUCCESS -> {
                     Timber.i("Loading grade stats result: Success")
                     view?.run {
-                        showEmpty(it.data!!.isEmpty() || it.data.first().partial.isEmpty())
-                        showContent(it.data.isNotEmpty() && it.data.first().partial.isNotEmpty())
+                        val isNoContent = it.data!!.isEmpty() || when (type) {
+                            ViewType.SEMESTER -> it.data.firstOrNull()?.semester?.amounts.orEmpty().sum() == 0
+                            ViewType.PARTIAL -> it.data.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0
+                            ViewType.POINTS -> it.data.firstOrNull()?.points?.let { points ->
+                                points.student == .0 && points.others == .0
+                            } ?: false
+                        }
+                        showEmpty(isNoContent)
+                        showContent(!isNoContent)
                         showErrorView(false)
                         updateData(it.data, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList)
                         showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemoteTest.kt
index cd2a3070..6d3d63a7 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemoteTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/gradestatistics/GradeStatisticsRemoteTest.kt
@@ -4,7 +4,8 @@ import io.github.wulkanowy.data.db.entities.Semester
 import io.github.wulkanowy.getStudentEntity
 import io.github.wulkanowy.sdk.Sdk
 import io.github.wulkanowy.sdk.pojo.GradePointsStatistics
-import io.github.wulkanowy.sdk.pojo.GradeStatistics
+import io.github.wulkanowy.sdk.pojo.GradeStatisticsItem
+import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject
 import io.github.wulkanowy.utils.init
 import io.mockk.MockKAnnotations
 import io.mockk.coEvery
@@ -35,8 +36,8 @@ class GradeStatisticsRemoteTest {
     @Test
     fun getGradeStatisticsTest() {
         coEvery { mockSdk.getGradesPartialStatistics(1) } returns listOf(
-            getGradeStatistics("Fizyka"),
-            getGradeStatistics("Matematyka")
+            getGradeStatisticsPartialSubject("Fizyka"),
+            getGradeStatisticsPartialSubject("Matematyka")
         )
 
         every { semesterMock.studentId } returns 1
@@ -45,7 +46,7 @@ class GradeStatisticsRemoteTest {
         every { semesterMock.semesterId } returns 1
         every { mockSdk.switchDiary(any(), any()) } returns mockSdk
 
-        val stats = runBlocking { GradeStatisticsRemote(mockSdk).getGradeStatistics(student, semesterMock, false) }
+        val stats = runBlocking { GradeStatisticsRemote(mockSdk).getGradePartialStatistics(student, semesterMock) }
         assertEquals(2, stats.size)
     }
 
@@ -66,19 +67,24 @@ class GradeStatisticsRemoteTest {
         assertEquals(2, stats.size)
     }
 
-    private fun getGradeStatistics(subjectName: String): GradeStatistics {
-        return GradeStatistics(
+    private fun getGradeStatisticsPartialSubject(subjectName: String): GradeStatisticsSubject {
+        return GradeStatisticsSubject(
             subject = subjectName,
-            gradeValue = 5,
-            amount = 10,
-            grade = "",
-            semesterId = 1
+            studentAverage = "",
+            classAverage = "",
+            classItems = listOf(
+                GradeStatisticsItem(
+                    subject = subjectName,
+                    grade = 0,
+                    amount = 0
+                )
+            ),
+            studentItems = listOf()
         )
     }
 
     private fun getGradePointsStatistics(subjectName: String): GradePointsStatistics {
         return GradePointsStatistics(
-            semesterId = 1,
             subject = subjectName,
             student = 0.80,
             others = 0.40

From 8a00ae95b899a41344fa9b3b932bce7dbf9e4b58 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stanis=C5=82aw=20Jelnicki?= <s.jelnicki@gmail.com>
Date: Fri, 13 Nov 2020 23:59:45 +0100
Subject: [PATCH 13/34] Update contributor's username (#1020)

---
 app/src/main/assets/contributors.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/src/main/assets/contributors.json b/app/src/main/assets/contributors.json
index 40aff4c4..54b47350 100644
--- a/app/src/main/assets/contributors.json
+++ b/app/src/main/assets/contributors.json
@@ -33,6 +33,6 @@
   },
   {
     "displayName": "Mateusz Idziejczak",
-    "githubUsername": "PanTajemnic"
+    "githubUsername": "Luncenok"
   }
 ]

From c7fdcc2bbdbf4f533688b56702cd313b18745778 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?= <RafalBO99@outlook.com>
Date: Tue, 17 Nov 2020 16:58:24 +0100
Subject: [PATCH 14/34] Add facebook link to about (#1021)

---
 .../github/wulkanowy/ui/modules/about/AboutFragment.kt |  9 +++++++++
 .../wulkanowy/ui/modules/about/AboutPresenter.kt       |  8 +++++++-
 .../io/github/wulkanowy/ui/modules/about/AboutView.kt  |  4 ++++
 app/src/main/res/drawable/ic_about_facebook.xml        | 10 ++++++++++
 app/src/main/res/values/strings.xml                    |  2 ++
 build.gradle                                           |  2 +-
 6 files changed, 33 insertions(+), 2 deletions(-)
 create mode 100644 app/src/main/res/drawable/ic_about_facebook.xml

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 5ec1a66e..0730033f 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
@@ -58,6 +58,11 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
             Triple(getString(R.string.about_discord), getString(R.string.about_discord_summary), getCompatDrawable(R.drawable.ic_about_discord))
         }
 
+    override val facebookRes: Triple<String, String, Drawable?>?
+        get() = context?.run {
+            Triple(getString(R.string.about_facebook), getString(R.string.about_facebook_summary), getCompatDrawable(R.drawable.ic_about_facebook))
+        }
+
     override val homepageRes: Triple<String, String, Drawable?>?
         get() = context?.run {
             Triple(getString(R.string.about_homepage), getString(R.string.about_homepage_summary), getCompatDrawable(R.drawable.ic_about_homepage))
@@ -113,6 +118,10 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
         context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
     }
 
+    override fun openFacebookPage() {
+        context?.openInternetBrowser("https://www.facebook.com/wulkanowy", ::showMessage)
+    }
+
     override fun openHomepage() {
         context?.openInternetBrowser("https://wulkanowy.github.io/", ::showMessage)
     }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt
index cd08b67e..76ba8212 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutPresenter.kt
@@ -3,8 +3,8 @@ package io.github.wulkanowy.ui.modules.about
 import io.github.wulkanowy.data.repositories.student.StudentRepository
 import io.github.wulkanowy.ui.base.BasePresenter
 import io.github.wulkanowy.ui.base.ErrorHandler
-import io.github.wulkanowy.utils.AppInfo
 import io.github.wulkanowy.utils.AnalyticsHelper
+import io.github.wulkanowy.utils.AppInfo
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -46,6 +46,11 @@ class AboutPresenter @Inject constructor(
                     openDiscordInvite()
                     analytics.logEvent("about_open", "name" to "discord")
                 }
+                facebookRes?.first -> {
+                    Timber.i("Opening facebook")
+                    openFacebookPage()
+                    analytics.logEvent("about_open", "name" to "facebook")
+                }
                 homepageRes?.first -> {
                     Timber.i("Opening homepage")
                     openHomepage()
@@ -78,6 +83,7 @@ class AboutPresenter @Inject constructor(
                 feedbackRes,
                 faqRes,
                 discordRes,
+                facebookRes,
                 homepageRes,
                 licensesRes,
                 privacyRes
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt
index 4c4b002f..54882b30 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/about/AboutView.kt
@@ -15,6 +15,8 @@ interface AboutView : BaseView {
 
     val discordRes: Triple<String, String, Drawable?>?
 
+    val facebookRes: Triple<String, String, Drawable?>?
+
     val homepageRes: Triple<String, String, Drawable?>?
 
     val licensesRes: Triple<String, String, Drawable?>?
@@ -31,6 +33,8 @@ interface AboutView : BaseView {
 
     fun openDiscordInvite()
 
+    fun openFacebookPage()
+
     fun openEmailClient()
 
     fun openFaqPage()
diff --git a/app/src/main/res/drawable/ic_about_facebook.xml b/app/src/main/res/drawable/ic_about_facebook.xml
new file mode 100644
index 00000000..a1b7b46e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_about_facebook.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="#FFF"
+        android:fillType="nonZero"
+        android:pathData="M12,2C6.4883,2 2,6.4883 2,12C2,17.5117 6.4883,22 12,22C17.5117,22 22,17.5117 22,12C22,6.4883 17.5117,2 12,2ZM12,4C16.4297,4 20,7.5703 20,12C20,16.0156 17.0664,19.3125 13.2188,19.8984L13.2188,14.3828L15.5469,14.3828L15.9102,12.0195L13.2188,12.0195L13.2188,10.7266C13.2188,9.7422 13.5391,8.8711 14.457,8.8711L15.9336,8.8711L15.9336,6.8047C15.6758,6.7734 15.125,6.6953 14.0898,6.6953C11.9258,6.6953 10.6523,7.8398 10.6523,10.4453L10.6523,12.0195L8.4258,12.0195L8.4258,14.3828L10.6523,14.3828L10.6523,19.8789C6.8711,19.2422 4,15.9688 4,12C4,7.5703 7.5703,4 12,4ZM12,4" />
+</vector>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index cc3dbe3c..7c77b735 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -351,6 +351,8 @@
     <string name="about_faq_summary">Read Frequently Asked Questions</string>
     <string name="about_discord">Discord server</string>
     <string name="about_discord_summary">Join the Wulkanowy community</string>
+    <string name="about_facebook">Facebook fanpage</string>
+    <string name="about_facebook_summary">Like our facebook fanpage</string>
     <string name="about_privacy">Privacy policy</string>
     <string name="about_privacy_summary">Rules for collecting personal data</string>
     <string name="about_homepage">Homepage</string>
diff --git a/build.gradle b/build.gradle
index 051fce64..c6a6da0b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,7 +13,7 @@ buildscript {
     }
     dependencies {
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
-        classpath 'com.android.tools.build:gradle:4.1.0'
+        classpath 'com.android.tools.build:gradle:4.1.1'
         classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
         classpath 'com.google.gms:google-services:4.3.4'
         classpath 'com.huawei.agconnect:agcp:1.4.1.300'

From 5e9853b04335b7fd98f781b8054fbd89c62ab911 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?= <RafalBO99@outlook.com>
Date: Sat, 21 Nov 2020 15:50:18 +0100
Subject: [PATCH 15/34] New Crowdin updates (#1022)

---
 app/src/main/res/values-cs-rCZ/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-uk/strings.xml     | 2 ++
 5 files changed, 10 insertions(+)

diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml
index dd474aec..44a03e3d 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs-rCZ/strings.xml
@@ -348,6 +348,8 @@
     <string name="about_faq_summary">Přečtěte si často kladené otázky</string>
     <string name="about_discord">Server Discord</string>
     <string name="about_discord_summary">Připojte se ke komunitě Wulkanowy</string>
+    <string name="about_facebook">Facebooková fanpage</string>
+    <string name="about_facebook_summary">Stejně jako naše facebooková fanpage</string>
     <string name="about_privacy">Zásady ochrany osobních údajů</string>
     <string name="about_privacy_summary">Pravidla pro shromažďování osobních údajů</string>
     <string name="about_homepage">Domovská stránka</string>
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index d885261c..a16b18e5 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -308,6 +308,8 @@
     <string name="about_faq_summary">Lesen Sie Häufig gestellte Fragen</string>
     <string name="about_discord">Discord server</string>
     <string name="about_discord_summary">Treten Sie der Wulkanowy-Gemeinschaft bei</string>
+    <string name="about_facebook">Facebook fanpage</string>
+    <string name="about_facebook_summary">Gefällt unsere Facebook-Fanpage</string>
     <string name="about_privacy">Datenschutzerklärung</string>
     <string name="about_privacy_summary">Regeln für die Sammlung persönlicher Daten</string>
     <string name="about_homepage">Startseite</string>
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 67f8bd1b..dc877eaf 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -348,6 +348,8 @@
     <string name="about_faq_summary">Zobacz Najczęściej Zadawane Pytania</string>
     <string name="about_discord">Serwer Discord</string>
     <string name="about_discord_summary">Dołącz do społeczności Wulkanowego</string>
+    <string name="about_facebook">Fanpage na Facebooku</string>
+    <string name="about_facebook_summary">Polub nasz fanpage na Facebooku</string>
     <string name="about_privacy">Polityka prywatności</string>
     <string name="about_privacy_summary">Zasady zbierania danych osobowych</string>
     <string name="about_homepage">Strona domowa</string>
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 5e260d99..e2620b60 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -348,6 +348,8 @@
     <string name="about_faq_summary">Часто задаваемые вопросы</string>
     <string name="about_discord">Сервер Discord</string>
     <string name="about_discord_summary">Присоединиться к сообществу приложения</string>
+    <string name="about_facebook">Facebook фан-страница</string>
+    <string name="about_facebook_summary">Поставьте лайк на нашей странице в Facebook</string>
     <string name="about_privacy">Политика приватности</string>
     <string name="about_privacy_summary">Правила хранения личных данных</string>
     <string name="about_homepage">Домашняя страница</string>
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 48da2784..cc26b24f 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -348,6 +348,8 @@
     <string name="about_faq_summary">Запитання, які часто задають</string>
     <string name="about_discord">Сервер Discord</string>
     <string name="about_discord_summary">Приєднатися до спільноти додатка</string>
+    <string name="about_facebook">Фен-сторінка Facebook</string>
+    <string name="about_facebook_summary">Вподобати нашу фансторінку у Facebook</string>
     <string name="about_privacy">Політика конфіденційності</string>
     <string name="about_privacy_summary">Правила зберігання особистих даних</string>
     <string name="about_homepage">Домашня сторінка</string>

From c675dc8b848991836b4e72c2b24c05426f200c21 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Thu, 3 Dec 2020 13:27:47 +0000
Subject: [PATCH 16/34] Bump kotlin_version from 1.4.10 to 1.4.20 (#1026)

---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index c6a6da0b..92f4e448 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
 buildscript {
     ext {
-        kotlin_version = '1.4.10'
+        kotlin_version = '1.4.20'
         about_libraries = '8.4.3'
         hilt_version = "2.29.1-alpha"
     }

From c42333cd35854639dd77a6ef90434744071058e4 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Thu, 3 Dec 2020 13:28:10 +0000
Subject: [PATCH 17/34] Bump firebase-crashlytics-gradle from 2.3.0 to 2.4.1
 (#1029)

---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index 92f4e448..cbc06193 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.4'
         classpath 'com.huawei.agconnect:agcp:1.4.1.300'
-        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
+        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
         classpath "com.github.triplet.gradle:play-publisher:2.8.0"
         classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.0"
         classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"

From 3c0dda9a82c13af9f94db34766fe0212b3e725cb Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Thu, 3 Dec 2020 13:29:49 +0000
Subject: [PATCH 18/34] Bump hianalytics from 5.0.4.301 to 5.0.5.301 (#1023)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index 0afaab01..6944276a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -206,7 +206,7 @@ dependencies {
     playImplementation 'com.google.android.play:core-ktx:1.8.1'
     playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
 
-    hmsImplementation 'com.huawei.hms:hianalytics:5.0.4.301'
+    hmsImplementation 'com.huawei.hms:hianalytics:5.0.5.301'
     hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.4.1.300'
 
     releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"

From 13906a7d62c9071e67cd7a65379adde1cae0637c Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Thu, 3 Dec 2020 13:47:26 +0000
Subject: [PATCH 19/34] Bump about_libraries from 8.4.3 to 8.6.2 (#1025)

---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index cbc06193..d0479a63 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
 buildscript {
     ext {
         kotlin_version = '1.4.20'
-        about_libraries = '8.4.3'
+        about_libraries = '8.6.2'
         hilt_version = "2.29.1-alpha"
     }
     repositories {

From 0fc828f006bbd2a51554cab88b39d834bf215e1c Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Thu, 3 Dec 2020 13:48:25 +0000
Subject: [PATCH 20/34] Bump agcp from 1.4.1.300 to 1.4.2.301 (#1024)

---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index d0479a63..6aa3d6f2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,7 +16,7 @@ buildscript {
         classpath 'com.android.tools.build:gradle:4.1.1'
         classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
         classpath 'com.google.gms:google-services:4.3.4'
-        classpath 'com.huawei.agconnect:agcp:1.4.1.300'
+        classpath 'com.huawei.agconnect:agcp:1.4.2.301'
         classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
         classpath "com.github.triplet.gradle:play-publisher:2.8.0"
         classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.0"

From 1428887204f286ccb99fda0a6e5fadad636bad67 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Thu, 3 Dec 2020 13:57:37 +0000
Subject: [PATCH 21/34] Bump agconnect-crash from 1.4.1.300 to 1.4.2.301
 (#1030)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index 6944276a..2d35af50 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -207,7 +207,7 @@ dependencies {
     playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
 
     hmsImplementation 'com.huawei.hms:hianalytics:5.0.5.301'
-    hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.4.1.300'
+    hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.4.2.301'
 
     releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
 

From 7ed478749644e9514a6623e5a752aa4912c416ed Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Thu, 3 Dec 2020 14:19:21 +0000
Subject: [PATCH 22/34] Bump chucker from 3.3.0 to 3.4.0 (#1027)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index 2d35af50..5957bd6a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -132,7 +132,7 @@ play {
 ext {
     work_manager = "2.4.0"
     room = "2.2.5"
-    chucker = "3.3.0"
+    chucker = "3.4.0"
     mockk = "1.10.2"
     moshi = "1.11.0"
 }

From f263b5534a5c57644738c4cae7bd9578e08bd3aa Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 6 Dec 2020 14:08:26 +0000
Subject: [PATCH 23/34] Bump mockk from 1.10.2 to 1.10.3-jdk8 (#1034)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index 5957bd6a..a8ee923f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -133,7 +133,7 @@ ext {
     work_manager = "2.4.0"
     room = "2.2.5"
     chucker = "3.4.0"
-    mockk = "1.10.2"
+    mockk = "1.10.3-jdk8"
     moshi = "1.11.0"
 }
 

From 41dbd2d25f0a32ac4d435d7e7e38deece2806d7e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?= <RafalBO99@outlook.com>
Date: Sun, 6 Dec 2020 15:14:58 +0100
Subject: [PATCH 24/34] New Crowdin updates (#1033)

---
 app/src/main/res/values-cs-rCZ/strings.xml | 100 ++++++++++-----------
 1 file changed, 50 insertions(+), 50 deletions(-)

diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml
index 44a03e3d..3769b851 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs-rCZ/strings.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <!--Activity/Fragment title-->
-    <string name="login_title">Přihlásit se</string>
+    <string name="login_title">Přihlášení</string>
     <string name="main_title">Wulkanowy</string>
     <string name="grade_title">Známky</string>
-    <string name="attendance_title">Prezence</string>
+    <string name="attendance_title">Docházka</string>
     <string name="exam_title">Zkoušky</string>
     <string name="timetable_title">Plán lekce</string>
     <string name="settings_title">Nastavení</string>
@@ -16,12 +16,12 @@
     <string name="message_title">Zprávy</string>
     <string name="send_message_title">Nová zpráva</string>
     <string name="note_title">Poznámky a úspěchy</string>
-    <string name="homework_title">Domácí práce</string>
+    <string name="homework_title">Domácí úkoly</string>
     <string name="account_title">Vyberte účet</string>
     <!--Subtitles-->
     <string name="grade_subtitle">Semestr %1$d, %2$d/%3$d</string>
     <!--Login-->
-    <string name="login_header_default">Přihlaste se pomocí studentského nebo nadřazeného účtu</string>
+    <string name="login_header_default">Přihlaste se pomocí studentského nebo rodičovského účtu</string>
     <string name="login_header_symbol">Zadejte symbol ze stránky deníku</string>
     <string name="login_nickname_hint">Uživatelské jméno</string>
     <string name="login_email_hint">Email</string>
@@ -41,14 +41,14 @@
     <string name="login_invalid_pin">Neplatný PIN</string>
     <string name="login_invalid_token">Neplatný token</string>
     <string name="login_expired_token">Platnost tokenu vypršela</string>
-    <string name="login_invalid_email">Nesprávná e-mailová adresa</string>
+    <string name="login_invalid_email">Neplatný e-mail</string>
     <string name="login_invalid_login">Místo e-mailu použijte přiřazené přihlašovací údaje</string>
     <string name="login_invalid_symbol">Neplatný symbol</string>
     <string name="login_incorrect_symbol">Student nebyl nalezen. Zkontrolujte správnost symbolu a vybrané varianty deníku UONET+</string>
     <string name="login_field_required">Toto pole je povinné</string>
     <string name="login_duplicate_student">Vybraný student je již přihlášen</string>
     <string name="login_symbol_helper">Symbol najdete na stránce deníku v &#160;<b>Uczeń</b> → &#160;<b>Dostęp Mobilny</b> → &#160;<b>Zarejestruj urządzenie mobilne</b>.\n\nUjistěte se, že jste na předchozí obrazovce nastavili správnou variantu deníku do pole <b>Variace deníku UONET+</b>. Wulkanowy v tuto chvíli nezjistí předškolní studenty</string>
-    <string name="login_select_student">Vyberte studenty k přihlášení do aplikace</string>
+    <string name="login_select_student">Vyberte studenty, kteří se mají do aplikace přihlásit</string>
     <string name="login_advanced">Jiné možnosti</string>
     <string name="login_advanced_warning_mobile_api">V tomto režimu nefungují následující: šťastné číslo, statistiky třídy, shrnutí docházky, ospravedlnění nepřítomnosti, absolvované lekce, informace o škole a prohlížení seznamu registrovaných zařízení</string>
     <string name="login_advanced_warning_scraper">Tento režim zobrazuje stejná data, která se zobrazují na webových stránkách deníka</string>
@@ -65,7 +65,7 @@
     <string name="login_recover">Obnovit</string>
     <string name="login_signed_in">Student je již přihlášen</string>
     <!--Main-->
-    <string name="main_account_picker">Správce účtu</string>
+    <string name="main_account_picker">Manažer účtů</string>
     <string name="main_log_in">Přihlásit se</string>
     <string name="main_session_expired">Platnost relace vypršela</string>
     <string name="main_session_relogin">Vaše relace vypršela, přihlaste se prosím znovu</string>
@@ -79,9 +79,9 @@
     <string name="grade_comment">Komentář</string>
     <string name="grade_no_new_items">Žádné nové známky</string>
     <string name="grade_number_new_items">Počet nových známek: %1$d</string>
-    <string name="grade_average">Průměrný: %1$.2f</string>
+    <string name="grade_average">Průměr: %1$.2f</string>
     <string name="grade_points_sum">Body: %s</string>
-    <string name="grade_no_average">Žádný průměr</string>
+    <string name="grade_no_average">Bez průměru</string>
     <string name="grade_predicted">Předpovězeno: %1$s</string>
     <string name="grade_final">Konečná: %1$s</string>
     <string name="grade_summary_points">Celkem bodů</string>
@@ -126,10 +126,10 @@
         <item quantity="other">Máte %1$d nové známky</item>
     </plurals>
     <plurals name="grade_notify_new_items_predicted">
-        <item quantity="one">Máte %1$d novou konečnou známku</item>
-        <item quantity="few">Máte %1$d nové konečné známky</item>
-        <item quantity="many">Máte %1$d nové konečné známky</item>
-        <item quantity="other">Máte %1$d nové konečné známky</item>
+        <item quantity="one">Máte %1$d novou předpokládanou známku</item>
+        <item quantity="few">Máte %1$d nové předpokládané známky</item>
+        <item quantity="many">Máte %1$d nové předpokládané známky</item>
+        <item quantity="other">Máte %1$d nové předpokládané známky</item>
     </plurals>
     <plurals name="grade_notify_new_items_final">
         <item quantity="one">Máte %1$d novou konečnou známku</item>
@@ -143,7 +143,7 @@
     <string name="timetable_group">Skupina</string>
     <string name="timetable_time">Hodiny</string>
     <string name="timetable_changes">Změny</string>
-    <string name="timetable_no_items">Dnes žádné lekce</string>
+    <string name="timetable_no_items">Žádné lekce tento den</string>
     <string name="timetable_minutes">%s min</string>
     <string name="timetable_seconds">%s sek</string>
     <string name="timetable_time_left">dosud %1$s</string>
@@ -155,32 +155,32 @@
     <!--Completed lessons-->
     <string name="completed_lessons_title">Dokončené lekce</string>
     <string name="completed_lessons_button">Zobrazit dokončené lekce</string>
-    <string name="completed_lessons_no_items">Žádné informace o absolvovaných lekcích</string>
+    <string name="completed_lessons_no_items">Žádné informace o dokončených lekcích</string>
     <string name="completed_lessons_topic">Téma</string>
-    <string name="completed_lessons_absence">Absence</string>
+    <string name="completed_lessons_absence">Nepřítomnost</string>
     <string name="completed_lessons_resources">Zdroje</string>
     <!--Attendance-->
     <string name="attendance_summary_button">Souhrn docházky</string>
     <string name="attendance_absence_school">Nepřítomen ze školních důvodů</string>
-    <string name="attendance_absence_excused">Omluvená absence</string>
+    <string name="attendance_absence_excused">Omluvená nepřítomnost</string>
     <string name="attendance_absence_unexcused">Neomluvená absence</string>
     <string name="attendance_exemption">Osvobození</string>
-    <string name="attendance_excused_lateness">Oprávněné zpoždění</string>
-    <string name="attendance_unexcused_lateness">Neomluvená zpoždění</string>
+    <string name="attendance_excused_lateness">Omluvené zpoždění</string>
+    <string name="attendance_unexcused_lateness">Neomluvené zpoždění</string>
     <string name="attendance_present">Přítomnost</string>
     <string name="attendance_deleted">Smazáno</string>
     <string name="attendance_unknown">Neznámý</string>
     <string name="attendance_number">Číslo lekce</string>
     <string name="attendance_no_items">Žádné položky</string>
     <plurals name="attendance_number_absences">
-        <item quantity="one">%1$d absence</item>
-        <item quantity="few">%1$d absence</item>
-        <item quantity="many">%1$d absence</item>
-        <item quantity="other">%1$d absence</item>
+        <item quantity="one">%1$d nepřítomnost</item>
+        <item quantity="few">%1$d nepřítomnosti</item>
+        <item quantity="many">%1$d nepřítomnosti</item>
+        <item quantity="other">%1$d nepřítomnosti</item>
     </plurals>
-    <string name="attendance_excuse_dialog_reason">Důvod absence (volitelný)</string>
+    <string name="attendance_excuse_dialog_reason">Důvod nepřítomnosti (volitelný)</string>
     <string name="attendance_excuse_dialog_submit">Poslat</string>
-    <string name="attendance_excuse_success">Absence úspěšně omluvena!</string>
+    <string name="attendance_excuse_success">Nepřítomnost úspěšně omluvena!</string>
     <string name="attendance_excuse_no_selection">Musíte vybrat alespoň jednu nepřítomnost!</string>
     <string name="attendance_excuse_title">Ospravedlnit</string>
     <!--Attendance summary-->
@@ -198,7 +198,7 @@
     <string name="message_no_items">Žádné zprávy</string>
     <string name="message_preview_error">Při stahování obsahu zprávy došlo k chybě</string>
     <string name="message_from">Od:</string>
-    <string name="message_to">Do:</string>
+    <string name="message_to">Komu:</string>
     <string name="message_date">Datum: %s</string>
     <string name="message_reply">Odpověď</string>
     <string name="message_forward">Poslat dále</string>
@@ -226,10 +226,10 @@
         <item quantity="other">Nové zprávy</item>
     </plurals>
     <plurals name="message_notify_new_items">
-        <item quantity="one">Obdrželi jste %1$d zprávu</item>
-        <item quantity="few">Obdrželi jste %1$d zpráv</item>
-        <item quantity="many">Obdrželi jste %1$d zpráv</item>
-        <item quantity="other">Obdrželi jste %1$d zpráv</item>
+        <item quantity="one">Máte %1$d novou zprávu</item>
+        <item quantity="few">Máte %1$d nové zprávy</item>
+        <item quantity="many">Máte %1$d nové zprávy</item>
+        <item quantity="other">Máte %1$d nové zprávy</item>
     </plurals>
     <!--Note-->
     <string name="note_no_items">Žádné informace o poznámkách</string>
@@ -286,12 +286,12 @@
     </plurals>
     <plurals name="neutral_note_notify_new_items">
         <item quantity="one">Máte %1$d novou neutrální pozornost</item>
-        <item quantity="few">Máte %1$d nové neutrální komentáře</item>
-        <item quantity="many">Máte %1$d nové neutrální komentáře</item>
-        <item quantity="other">Máte %1$d nové neutrální komentáře</item>
+        <item quantity="few">Máte %1$d nové neutrální 	pozornosti</item>
+        <item quantity="many">Máte %1$d nové neutrální 	pozornosti</item>
+        <item quantity="other">Máte %1$d nové neutrální 	pozornosti</item>
     </plurals>
     <!--Homework-->
-    <string name="homework_no_items">Žádný domácí úkol</string>
+    <string name="homework_no_items">Žádné informace o domácím úkolu</string>
     <string name="homework_mark_as_done">Označit jako hotové</string>
     <string name="homework_mark_as_undone">Neudělané</string>
     <string name="homework_attachments">Přílohy</string>
@@ -315,7 +315,7 @@
     <!--School-->
     <string name="school_title">Škola</string>
     <string name="school_no_info">Žádné informace o škole</string>
-    <string name="school_name">Školní jméno</string>
+    <string name="school_name">Název školy</string>
     <string name="school_address">Adresa školy</string>
     <string name="school_telephone">Telefon</string>
     <string name="school_headmaster">Jméno ředitele</string>
@@ -336,14 +336,14 @@
     <string name="account_logout_student">Odhlášení studentů</string>
     <string name="account_type_student">Studentský účet</string>
     <string name="account_type_parent">Rodičovský účet</string>
-    <string name="account_login_mobile_api">Režimu Mobíle API</string>
+    <string name="account_login_mobile_api">Režim Mobilního API</string>
     <string name="account_login_hybrid">Hybridní režim</string>
     <!--About-->
     <string name="about_version">Verze aplikace</string>
     <string name="about_contributor">Tvůrci</string>
     <string name="about_contributor_summary">Seznam vývojářů Wulkanowy</string>
     <string name="about_feedback">Nahlásit chybu</string>
-    <string name="about_feedback_summary">Pošlete hlášení o chybě e-mailem</string>
+    <string name="about_feedback_summary">Odeslat zprávu o chybě e-mailem</string>
     <string name="about_faq">FAQ</string>
     <string name="about_faq_summary">Přečtěte si často kladené otázky</string>
     <string name="about_discord">Server Discord</string>
@@ -365,8 +365,8 @@
     <string name="logviewer_share">Sdílejte protokoly</string>
     <string name="logviewer_refresh">Obnovit</string>
     <!--Error dialog-->
-    <string name="dialog_error_check_update">Kontrola aktualizací</string>
-    <string name="dialog_error_check_update_message">Před nahlášením chyby nejprve zkontrolujte, zda je k dispozici aktualizace s opravou chyby</string>
+    <string name="dialog_error_check_update">Zkontrolovat aktualizace</string>
+    <string name="dialog_error_check_update_message">Před hlášením chyby zkontrolujte, zda je k dispozici aktualizace s opravou chyb</string>
     <!--Generic-->
     <string name="all_content">Obsah</string>
     <string name="all_retry">Zkuste to znovu</string>
@@ -383,21 +383,21 @@
     <string name="all_subject">Předmět</string>
     <string name="all_prev">Předchozí</string>
     <string name="all_next">Další</string>
-    <string name="all_search">Vyhledávání</string>
-    <string name="all_search_hint">Vyhledávání…</string>
+    <string name="all_search">Hledat</string>
+    <string name="all_search_hint">Hledat…</string>
     <!--Timetable Widget-->
     <string name="widget_timetable_no_items">Žádné lekce</string>
-    <string name="widget_timetable_theme_title">Vyberte téma</string>
+    <string name="widget_timetable_theme_title">Vybrat motiv</string>
     <string name="widget_timetable_theme_light">Světlý</string>
     <string name="widget_timetable_theme_dark">Tmavý</string>
-    <string name="widget_timetable_theme_system">Téma systému</string>
+    <string name="widget_timetable_theme_system">Motiv systému</string>
     <!--Preferences-->
     <string name="pref_view_header">Vzhled</string>
     <string name="pref_view_list">Výchozí zobrazení</string>
     <string name="pref_view_grade_average_mode">Výpočet koncoročního průměru</string>
     <string name="pref_view_grade_average_force_calc">Vynutit průměrný výpočet podle aplikace</string>
     <string name="pref_view_present">Zobrazit přítomnost v účasti</string>
-    <string name="pref_view_app_theme">Téma aplikace</string>
+    <string name="pref_view_app_theme">Motiv aplikace</string>
     <string name="pref_view_expand_grade">Rozbalit známky</string>
     <string name="pref_view_timetable_show_timers">Označte aktuální lekci v plánu lekce</string>
     <string name="pref_view_timetable_show_groups">Ukázat skupiny vedle předmětů v plánu lekce</string>
@@ -412,22 +412,22 @@
     <string name="pref_notify_upcoming_lessons_switch">Ukázat nadcházející oznámení o lekci</string>
     <string name="pref_notify_fix_sync_issues">Opravte problémy se synchronizací a upozorněním</string>
     <string name="pref_notify_fix_sync_issues_message">Ve vašem zařízení mohou nastat problémy se synchronizací dat a oznámenímii.\n\nChcete-li je opravit, přidejte Wulkanowy do funkce Autostart a vypněte optimalizaci/úsporu baterie v nastavení systému telefonu.</string>
-    <string name="pref_notify_fix_sync_issues_settings_button">Jdi do nastavení</string>
+    <string name="pref_notify_fix_sync_issues_settings_button">Přejít do nastavení</string>
     <string name="pref_notify_debug_switch">Ukázat oznámení o ladění</string>
     <string name="pref_services_header">Synchronizace</string>
     <string name="pref_services_switch">Automatická aktualizace</string>
     <string name="pref_services_suspended">Pozastaveno na dovolené</string>
     <string name="pref_services_interval">Interval aktualizací</string>
     <string name="pref_services_wifi">Pouze Wi-Fi</string>
-    <string name="pref_services_force_sync">Nyní synchronizovat</string>
+    <string name="pref_services_force_sync">Synchronizovat nyní</string>
     <string name="pref_services_message_sync_success">Synchronizováno!</string>
-    <string name="pref_services_message_sync_failed">Synchronizace se nezdařila</string>
+    <string name="pref_services_message_sync_failed">Synchronizace selhala</string>
     <string name="pref_services_sync_in_progress">Probíhá synchronizace</string>
     <string name="pref_services_dialog_force_sync_title">Synchronizace</string>
     <string name="pref_services_dialog_force_sync_summary">        Ruční synchronizace neobnoví zobrazení aplikace.
         \nChcete-li zobrazit synchronizovaná data, restartujte aplikaci po synchronizaci.
     </string>
-    <string name="pref_other_header">Jiný</string>
+    <string name="pref_other_header">Jiné</string>
     <string name="pref_other_grade_modifier_plus">Hodnota plusu</string>
     <string name="pref_other_grade_modifier_minus">Hodnota mínusu</string>
     <string name="pref_other_fill_message_content">Odpovědět s historií zpráv</string>
@@ -437,9 +437,9 @@
     <string name="channel_lucky_number">Šťastné číslo</string>
     <string name="channel_new_message">Nové zprávy</string>
     <string name="channel_new_notes">Nové poznámky</string>
-    <string name="channel_push">Oznámení push</string>
+    <string name="channel_push">Push oznámení</string>
     <string name="channel_upcoming_lessons">Nadcházející lekce</string>
-    <string name="channel_debug">Ladění</string>
+    <string name="channel_debug">Debug</string>
     <!--Colors-->
     <string name="all_black">Černý</string>
     <string name="all_red">Červený</string>

From 40ec5bbe867846d611e495301ac832ec0e03be21 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 6 Dec 2020 14:29:32 +0000
Subject: [PATCH 25/34] Bump kotlinx-coroutines-android from 1.4.0 to
 1.4.2-native-mt (#1032)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index a8ee923f..02c121f9 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -147,7 +147,7 @@ dependencies {
     coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
 
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
-    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'
+    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
 
     implementation "androidx.core:core-ktx:1.3.2"
     implementation "androidx.activity:activity-ktx:1.1.0"

From bf342ed28947e5b5702b515eba9adaa089e52dbd Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 6 Dec 2020 14:32:08 +0000
Subject: [PATCH 26/34] Bump kotlinx-coroutines-test from 1.4.0 to
 1.4.2-native-mt (#1031)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index 02c121f9..22d24083 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -216,7 +216,7 @@ dependencies {
 
     testImplementation "junit:junit:4.13.1"
     testImplementation "io.mockk:mockk:$mockk"
-    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.0'
+    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
 
     androidTestImplementation "androidx.test:core:1.3.0"
     androidTestImplementation "androidx.test:runner:1.3.0"

From ce802cc737088ffd3013854f18a387e3940f7aac Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 6 Dec 2020 14:43:48 +0000
Subject: [PATCH 27/34] Bump about_libraries from 8.6.2 to 8.6.3 (#1037)

---
 build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle b/build.gradle
index 6aa3d6f2..84d1d236 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
 buildscript {
     ext {
         kotlin_version = '1.4.20'
-        about_libraries = '8.6.2'
+        about_libraries = '8.6.3'
         hilt_version = "2.29.1-alpha"
     }
     repositories {

From e637896ad3c971ea552619bafd3b5a6829d15786 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 6 Dec 2020 14:50:57 +0000
Subject: [PATCH 28/34] Bump firebase-crashlytics from 17.2.2 to 17.3.0 (#1038)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index 22d24083..3e4bbfe0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -202,7 +202,7 @@ dependencies {
     playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.2'
     playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.2"
     playImplementation 'com.google.firebase:firebase-messaging:21.0.0'
-    playImplementation 'com.google.firebase:firebase-crashlytics:17.2.2'
+    playImplementation 'com.google.firebase:firebase-crashlytics:17.3.0'
     playImplementation 'com.google.android.play:core-ktx:1.8.1'
     playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
 

From d115372c3bc9c197aeec882139426302686249a1 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 6 Dec 2020 14:57:50 +0000
Subject: [PATCH 29/34] Bump coil from 1.0.0 to 1.1.0 (#1040)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index 3e4bbfe0..cd7a1bcb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -194,7 +194,7 @@ dependencies {
     implementation "fr.bipi.treessence:treessence:0.3.2"
     implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
     implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
-    implementation "io.coil-kt:coil:1.0.0"
+    implementation "io.coil-kt:coil:1.1.0"
     implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
     implementation 'me.xdrop:fuzzywuzzy:1.3.1'
 

From 33d540e1c9b6af3953cbba00af95c3d9fcba61e6 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 6 Dec 2020 15:04:43 +0000
Subject: [PATCH 30/34] Bump desugar_jdk_libs from 1.0.10 to 1.1.1 (#1039)

---
 app/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/app/build.gradle b/app/build.gradle
index cd7a1bcb..c85af3c7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -144,7 +144,7 @@ configurations.all {
 dependencies {
     implementation "io.github.wulkanowy:sdk:bcf6b53"
 
-    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
+    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
 
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
     implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'

From 05a597313b4898c8953a5f2ab9e9caea2b95a439 Mon Sep 17 00:00:00 2001
From: "dependabot-preview[bot]"
 <27856297+dependabot-preview[bot]@users.noreply.github.com>
Date: Sun, 6 Dec 2020 17:54:09 +0000
Subject: [PATCH 31/34] Bump hilt_version from 2.29.1-alpha to 2.30.1-alpha
 (#1035)

---
 .../main/java/io/github/wulkanowy/data/RepositoryModule.kt    | 4 ++--
 app/src/main/java/io/github/wulkanowy/di/AppModule.kt         | 4 ++--
 .../main/java/io/github/wulkanowy/services/ServicesModule.kt  | 4 ++--
 build.gradle                                                  | 2 +-
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
index 5efe7773..e8a3fa48 100644
--- a/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/RepositoryModule.kt
@@ -11,8 +11,8 @@ import com.chuckerteam.chucker.api.RetentionManager
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn
-import dagger.hilt.android.components.ApplicationComponent
 import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
 import io.github.wulkanowy.data.db.AppDatabase
 import io.github.wulkanowy.data.db.SharedPrefProvider
 import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
@@ -21,7 +21,7 @@ import timber.log.Timber
 import javax.inject.Singleton
 
 @Module
-@InstallIn(ApplicationComponent::class)
+@InstallIn(SingletonComponent::class)
 internal class RepositoryModule {
 
     @Singleton
diff --git a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt
index 9133c34e..4efb4d84 100644
--- a/app/src/main/java/io/github/wulkanowy/di/AppModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/di/AppModule.kt
@@ -6,13 +6,13 @@ import com.yariksoffice.lingver.Lingver
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn
-import dagger.hilt.android.components.ApplicationComponent
 import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
 import io.github.wulkanowy.utils.DispatchersProvider
 import javax.inject.Singleton
 
 @Module
-@InstallIn(ApplicationComponent::class)
+@InstallIn(SingletonComponent::class)
 internal class AppModule {
 
     @Singleton
diff --git a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt
index ac5a84e8..891f07da 100644
--- a/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/ServicesModule.kt
@@ -9,8 +9,8 @@ import dagger.Binds
 import dagger.Module
 import dagger.Provides
 import dagger.hilt.InstallIn
-import dagger.hilt.android.components.ApplicationComponent
 import dagger.hilt.android.qualifiers.ApplicationContext
+import dagger.hilt.components.SingletonComponent
 import dagger.multibindings.IntoSet
 import io.github.wulkanowy.services.sync.channels.Channel
 import io.github.wulkanowy.services.sync.channels.DebugChannel
@@ -38,7 +38,7 @@ import javax.inject.Singleton
 
 @Suppress("unused")
 @Module
-@InstallIn(ApplicationComponent::class)
+@InstallIn(SingletonComponent::class)
 abstract class ServicesModule {
 
     companion object {
diff --git a/build.gradle b/build.gradle
index 84d1d236..a68efccd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ buildscript {
     ext {
         kotlin_version = '1.4.20'
         about_libraries = '8.6.3'
-        hilt_version = "2.29.1-alpha"
+        hilt_version = "2.30.1-alpha"
     }
     repositories {
         mavenCentral()

From 6ca5e11371e65b27d16effd6582cce4c1aa4d64a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?= <RafalBO99@outlook.com>
Date: Sun, 6 Dec 2020 19:31:35 +0100
Subject: [PATCH 32/34] Fix HMS analytics and crashlytics (#1042)

---
 .../io/github/wulkanowy/utils/AnalyticsHelper.kt  |  4 ++++
 .../io/github/wulkanowy/utils/AnalyticsHelper.kt  |  9 +++++----
 .../io/github/wulkanowy/utils/CrashLogUtils.kt    |  4 ++--
 .../wulkanowy/ui/base/BaseDialogFragment.kt       | 15 +++++++++++++++
 .../wulkanowy/ui/modules/main/MainActivity.kt     | 14 ++++++++------
 .../wulkanowy/ui/modules/main/MainPresenter.kt    |  3 +--
 .../github/wulkanowy/ui/modules/main/MainView.kt  |  2 --
 .../io/github/wulkanowy/utils/AnalyticsHelper.kt  |  4 ++++
 8 files changed, 39 insertions(+), 16 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 0cd9a52e..3bf7e169 100644
--- a/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
+++ b/app/src/fdroid/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
@@ -15,4 +15,8 @@ class AnalyticsHelper @Inject constructor() {
     fun setCurrentScreen(activity: Activity, name: String?) {
         // do nothing
     }
+
+    fun popCurrentScreen(name: String?) {
+        // do nothing
+    }
 }
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 b3cecf24..5d33825f 100644
--- a/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
+++ b/app/src/hms/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
@@ -30,9 +30,10 @@ class AnalyticsHelper @Inject constructor(
     }
 
     fun setCurrentScreen(activity: Activity, name: String?) {
-        analytics.onEvent("screen_view", Bundle().apply {
-            putString("screen_name", name)
-            putString("screen_class", activity::class.simpleName)
-        })
+        analytics.pageStart(name, activity::class.simpleName)
+    }
+
+    fun popCurrentScreen(name: String?) {
+        analytics.pageEnd(name)
     }
 }
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 8adf2b29..60588f63 100644
--- a/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt
+++ b/app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt
@@ -44,9 +44,9 @@ class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR) {
         connectCrash.setCustomKey("message", message)
 
         if (t != null) {
-            connectCrash.log(priority, t.stackTraceToString())
+            connectCrash.recordException(t)
         } else {
-            connectCrash.log(priority, StackTraceRecorder(format(priority, tag, message)).stackTraceToString())
+            connectCrash.recordException(StackTraceRecorder(format(priority, tag, message)))
         }
     }
 }
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt
index 18cd58b4..1c31976e 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseDialogFragment.kt
@@ -3,10 +3,15 @@ package io.github.wulkanowy.ui.base
 import android.widget.Toast
 import androidx.fragment.app.DialogFragment
 import androidx.viewbinding.ViewBinding
+import io.github.wulkanowy.utils.AnalyticsHelper
 import io.github.wulkanowy.utils.lifecycleAwareVariable
+import javax.inject.Inject
 
 abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment(), BaseView {
 
+    @Inject
+    lateinit var analyticsHelper: AnalyticsHelper
+
     protected var binding: VB by lifecycleAwareVariable()
 
     override fun showError(text: String, error: Throwable) {
@@ -28,4 +33,14 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment(), BaseView
     override fun showErrorDetailsDialog(error: Throwable) {
         ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
     }
+
+    override fun onResume() {
+        super.onResume()
+        analyticsHelper.setCurrentScreen(requireActivity(), this::class.simpleName)
+    }
+
+    override fun onPause() {
+        super.onPause()
+        analyticsHelper.popCurrentScreen(this::class.simpleName)
+    }
 }
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 245f3b16..25b41aab 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
@@ -38,8 +38,8 @@ import io.github.wulkanowy.ui.modules.message.MessageFragment
 import io.github.wulkanowy.ui.modules.more.MoreFragment
 import io.github.wulkanowy.ui.modules.note.NoteFragment
 import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
-import io.github.wulkanowy.utils.AppInfo
 import io.github.wulkanowy.utils.AnalyticsHelper
+import io.github.wulkanowy.utils.AppInfo
 import io.github.wulkanowy.utils.UpdateHelper
 import io.github.wulkanowy.utils.dpToPx
 import io.github.wulkanowy.utils.getThemeAttrColor
@@ -182,7 +182,10 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
         }
 
         with(navController) {
-            setOnViewChangeListener(presenter::onViewChange)
+            setOnViewChangeListener { section, name ->
+                analytics.setCurrentScreen(this@MainActivity, name)
+                presenter.onViewChange(section)
+            }
             fragmentHideStrategy = HIDE
             rootFragments = listOf(
                 GradeFragment.newInstance(),
@@ -194,10 +197,6 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
         }
     }
 
-    override fun setCurrentScreen(name: String?) {
-        analytics.setCurrentScreen(this, name)
-    }
-
     override fun onOptionsItemSelected(item: MenuItem): Boolean {
         return if (item.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected()
         else false
@@ -208,6 +207,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
     }
 
     override fun switchMenuView(position: Int) {
+        analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
         navController.switchTab(position)
     }
 
@@ -245,10 +245,12 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
     }
 
     fun pushView(fragment: Fragment) {
+        analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
         navController.pushFragment(fragment)
     }
 
     override fun popView(depth: Int) {
+        analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
         navController.safelyPopFragments(depth)
     }
 
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 7ea8197e..cc525c29 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
@@ -35,9 +35,8 @@ class MainPresenter @Inject constructor(
         analytics.logEvent("app_open", "destination" to initMenu?.name)
     }
 
-    fun onViewChange(section: MainView.Section?, name: String?) {
+    fun onViewChange(section: MainView.Section?) {
         view?.apply {
-            setCurrentScreen(name)
             showActionBarElevation(section != GRADE && section != MESSAGE && section != SCHOOL)
             currentViewTitle?.let { setViewTitle(it) }
             currentViewSubtitle?.let { setViewSubTitle(it.ifBlank { null }) }
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 7e583147..97b556e3 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
@@ -24,8 +24,6 @@ interface MainView : BaseView {
 
     fun showAccountPicker()
 
-    fun setCurrentScreen(name: String?)
-
     fun showActionBarElevation(show: Boolean)
 
     fun notifyMenuViewReselected()
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 8897f180..ba29e1cb 100644
--- a/app/src/play/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
+++ b/app/src/play/java/io/github/wulkanowy/utils/AnalyticsHelper.kt
@@ -35,4 +35,8 @@ class AnalyticsHelper @Inject constructor(
             putString(FirebaseAnalytics.Param.SCREEN_CLASS, activity::class.simpleName)
         })
     }
+
+    @Suppress("UNUSED_PARAMETER")
+    fun popCurrentScreen(name: String?) {
+    }
 }

From 67cef0f6d9062129bfd3410f051c29c21a846001 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?= <m.pich@outlook.com>
Date: Sun, 6 Dec 2020 21:23:02 +0100
Subject: [PATCH 33/34] Add register variant auto-matching based on email
 (#1041)

---
 app/build.gradle                              |  2 +-
 .../repositories/timetable/TimetableRemote.kt |  2 +-
 .../modules/login/form/LoginFormFragment.kt   |  8 ++++++
 .../modules/login/form/LoginFormPresenter.kt  | 17 +++++++++---
 .../ui/modules/login/form/LoginFormView.kt    |  4 +++
 app/src/main/res/values/api_hosts.xml         | 26 +++++++++----------
 .../timetable/TimetableRemoteTest.kt          |  4 +--
 7 files changed, 43 insertions(+), 20 deletions(-)

diff --git a/app/build.gradle b/app/build.gradle
index c85af3c7..8dde9e7c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -142,7 +142,7 @@ configurations.all {
 }
 
 dependencies {
-    implementation "io.github.wulkanowy:sdk:bcf6b53"
+    implementation "io.github.wulkanowy:sdk:50c049ef"
 
     coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
 
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt
index eef8729e..9fb80e77 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemote.kt
@@ -14,7 +14,7 @@ class TimetableRemote @Inject constructor(private val sdk: Sdk) {
 
     suspend fun getTimetable(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Timetable> {
         return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
-            .getTimetable(startDate, endDate)
+            .getTimetable(startDate, endDate).first
             .map {
                 Timetable(
                     studentId = semester.studentId,
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 41345686..c6d0209d 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
@@ -89,6 +89,8 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
         }
     }
 
+    override fun getHostsValues(): List<String> = hostValues.toList()
+
     override fun setCredentials(username: String, pass: String) {
         with(binding) {
             loginFormUsername.setText(username)
@@ -96,6 +98,12 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
         }
     }
 
+    override fun setHost(host: String) {
+        binding.loginFormHost.setText(
+            hostKeys.getOrNull(hostValues.indexOf(host)).orEmpty()
+        )
+    }
+
     override fun setUsernameLabel(label: String) {
         binding.loginFormUsernameLayout.hint = label
     }
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 3e77cd77..35c79bb5 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,5 +1,6 @@
 package io.github.wulkanowy.ui.modules.login.form
 
+import androidx.core.net.toUri
 import io.github.wulkanowy.data.Status
 import io.github.wulkanowy.data.repositories.student.StudentRepository
 import io.github.wulkanowy.ui.base.BasePresenter
@@ -56,7 +57,7 @@ class LoginFormPresenter @Inject constructor(
 
     fun updateUsernameLabel() {
         view?.run {
-            setUsernameLabel(if ("standard" in formHostValue) emailLabel else nicknameLabel)
+            setUsernameLabel(if ("login" in formHostValue) nicknameLabel else emailLabel)
         }
     }
 
@@ -66,6 +67,16 @@ class LoginFormPresenter @Inject constructor(
 
     fun onUsernameTextChanged() {
         view?.clearUsernameError()
+
+        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 usernameHost = username.substringAfter("@")
+
+            hosts[usernameHost]?.let {
+                view?.setHost(it)
+            }
+        }
     }
 
     fun onSignInClick() {
@@ -135,12 +146,12 @@ class LoginFormPresenter @Inject constructor(
             view?.setErrorUsernameRequired()
             isCorrect = false
         } else {
-            if ("@" in login && "standard" !in host) {
+            if ("@" in login && "login" in host) {
                 view?.setErrorLoginRequired()
                 isCorrect = false
             }
 
-            if ("@" !in login && "standard" in host) {
+            if ("@" !in login && "email" in host) {
                 view?.setErrorEmailRequired()
                 isCorrect = false
             }
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 77e66538..31f8a621 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
@@ -19,8 +19,12 @@ interface LoginFormView : BaseView {
 
     val emailLabel: String
 
+    fun getHostsValues(): List<String>
+
     fun setCredentials(username: String, pass: String)
 
+    fun setHost(host: String)
+
     fun setUsernameLabel(label: String)
 
     fun setErrorUsernameRequired()
diff --git a/app/src/main/res/values/api_hosts.xml b/app/src/main/res/values/api_hosts.xml
index 29434602..0a43612d 100644
--- a/app/src/main/res/values/api_hosts.xml
+++ b/app/src/main/res/values/api_hosts.xml
@@ -22,25 +22,25 @@
         <item>Fakelog</item>
     </string-array>
     <string-array name="hosts_values">
-        <item>https://vulcan.net.pl/?standard</item>
+        <item>https://vulcan.net.pl/?email</item>
         <item>https://eszkola.opolskie.pl</item>
         <item>https://edu.gdansk.pl</item>
         <item>https://edu.lublin.eu</item>
         <item>https://umt.tarnow.pl</item>
         <item>https://resman.pl</item>
         <item>https://eduportal.koszalin.pl</item>
-        <item>https://vulcan.net.pl/</item>
-        <item>https://vulcan.net.pl/</item>
-        <item>https://vulcan.net.pl/</item>
-        <item>https://vulcan.net.pl/</item>
-        <item>https://vulcan.net.pl/</item>
-        <item>https://vulcan.net.pl/</item>
-        <item>https://vulcan.net.pl/</item>
-        <item>https://vulcan.net.pl/</item>
-        <item>https://vulcan.net.pl/</item>
-        <item>https://vulcan.net.pl/</item>
-        <item>https://vulcan.net.pl/</item>
-        <item>http://fakelog.tk/?standard</item>
+        <item>https://vulcan.net.pl/?login</item>
+        <item>https://vulcan.net.pl/?login</item>
+        <item>https://vulcan.net.pl/?login</item>
+        <item>https://vulcan.net.pl/?login</item>
+        <item>https://vulcan.net.pl/?login</item>
+        <item>https://vulcan.net.pl/?login</item>
+        <item>https://vulcan.net.pl/?login</item>
+        <item>https://vulcan.net.pl/?login</item>
+        <item>https://vulcan.net.pl/?login</item>
+        <item>https://vulcan.net.pl/?login</item>
+        <item>https://vulcan.net.pl/?login</item>
+        <item>http://fakelog.cf/?email</item>
     </string-array>
     <string-array name="hosts_symbols">
         <item>Default</item>
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemoteTest.kt
index c948ba31..a333e0d7 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemoteTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/timetable/TimetableRemoteTest.kt
@@ -39,10 +39,10 @@ class TimetableRemoteTest {
                 of(2018, 9, 10),
                 of(2018, 9, 15)
             )
-        } returns listOf(
+        } returns (listOf(
             getTimetable(of(2018, 9, 10)),
             getTimetable(of(2018, 9, 17))
-        )
+        ) to emptyList())
 
         every { semesterMock.studentId } returns 1
         every { semesterMock.diaryId } returns 1

From 73a92497ede9b1c32a7fd059655539b7e6cf1167 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?= <m.pich@outlook.com>
Date: Sun, 6 Dec 2020 22:12:18 +0100
Subject: [PATCH 34/34] Version 0.23.0

---
 .travis.yml                                       | 2 +-
 app/build.gradle                                  | 8 ++++----
 app/src/main/play/release-notes/pl-PL/default.txt | 9 +++++----
 3 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 26abd202..1fb3df0c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,7 +14,7 @@ cache:
 branches:
   only:
     - develop
-    - 0.22.2
+    - 0.23.0
 
 android:
  licenses:
diff --git a/app/build.gradle b/app/build.gradle
index 8dde9e7c..e5cfdcb2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -18,8 +18,8 @@ android {
         testApplicationId "io.github.tests.wulkanowy"
         minSdkVersion 17
         targetSdkVersion 30
-        versionCode 75
-        versionName "0.22.2"
+        versionCode 76
+        versionName "0.23.0"
         multiDexEnabled true
         resValue "string", "app_name", "Wulkanowy"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -126,7 +126,7 @@ play {
     serviceAccountCredentials = file('key.p12')
     defaultToAppBundles = false
     track = 'alpha'
-    updatePriority = 1
+    updatePriority = 3
 }
 
 ext {
@@ -142,7 +142,7 @@ configurations.all {
 }
 
 dependencies {
-    implementation "io.github.wulkanowy:sdk:50c049ef"
+    implementation "io.github.wulkanowy:sdk:0.23.0"
 
     coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
 
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 23fd77bc..32bce4a9 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 0.22.2
-- naprawiliśmy problem z wyświetlaniem pozycji na liście ocen
-- zmieniliśmy komunikaty o błędach, które powinny być teraz czytelniejsze dla większej liczby użytkowników
-- zwiększyliśmy maksymalny czas, przez który aplikacja będzie próbowała łączyć się z dziennikiem do 1 minuty
+Wersja 0.23.0
+- naprawiliśmy logowanie do dziennika Gdańskiej i Koszalińskiej Platformy Edukacyjnej
+- przy logowaniu odpowiednia odmiana dziennika jest ustawiana automatycznie na podstawie wpisanego adresu email
+- dodaliśmy wyświetlanie średniej w ocenach klasy (jeśli włączone przez szkołę)
+- dodaliśmy wyświetlanie zebrań
 
 Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases