From 31854fc4b86f3b66f63720709d423a62fa13b2c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sun, 25 Feb 2024 16:35:56 +0100
Subject: [PATCH 01/25] Fix text cut off across the app when text size is set
to 200% (#2435)
---
app/src/main/res/layout/activity_main.xml | 2 +-
.../main/res/layout/dialog_account_edit.xml | 6 ++--
.../main/res/layout/dialog_additional_add.xml | 6 ++--
app/src/main/res/layout/dialog_attendance.xml | 3 +-
app/src/main/res/layout/dialog_conference.xml | 3 +-
app/src/main/res/layout/dialog_exam.xml | 6 ++--
app/src/main/res/layout/dialog_grade.xml | 3 +-
app/src/main/res/layout/dialog_homework.xml | 6 ++--
.../main/res/layout/dialog_homework_add.xml | 6 ++--
.../res/layout/dialog_lesson_completed.xml | 3 +-
.../main/res/layout/dialog_mobile_device.xml | 18 +++++-----
app/src/main/res/layout/dialog_note.xml | 3 +-
.../res/layout/dialog_school_announcement.xml | 3 +-
app/src/main/res/layout/dialog_timetable.xml | 3 +-
.../main/res/layout/header_grade_details.xml | 15 +++++++++
app/src/main/res/layout/item_timetable.xml | 33 ++++++++++++-------
.../layout/subitem_dashboard_small_grade.xml | 4 +++
17 files changed, 86 insertions(+), 37 deletions(-)
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index d14de50a..a9284234 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -16,7 +16,7 @@
+ android:layout_height="wrap_content" />
diff --git a/app/src/main/res/layout/dialog_homework.xml b/app/src/main/res/layout/dialog_homework.xml
index 8c6cf0a7..10b71907 100644
--- a/app/src/main/res/layout/dialog_homework.xml
+++ b/app/src/main/res/layout/dialog_homework.xml
@@ -27,7 +27,7 @@
android:id="@+id/homeworkDialogRead"
style="@style/Widget.Material3.Button.TextButton.Dialog"
android:layout_width="wrap_content"
- android:layout_height="36dp"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="8dp"
android:layout_marginBottom="24dp"
@@ -35,6 +35,7 @@
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
+ android:minHeight="36dp"
android:text="@string/homework_mark_as_done"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/homeworkDialogClose" />
@@ -43,13 +44,14 @@
android:id="@+id/homeworkDialogClose"
style="@style/Widget.Material3.Button.TextButton.Dialog"
android:layout_width="wrap_content"
- android:layout_height="36dp"
+ android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginBottom="24dp"
android:insetLeft="0dp"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
+ android:minHeight="36dp"
android:text="@string/all_close"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
diff --git a/app/src/main/res/layout/dialog_homework_add.xml b/app/src/main/res/layout/dialog_homework_add.xml
index e0ff5b74..dc7ae32d 100644
--- a/app/src/main/res/layout/dialog_homework_add.xml
+++ b/app/src/main/res/layout/dialog_homework_add.xml
@@ -94,7 +94,7 @@
android:id="@+id/homeworkDialogClose"
style="@style/Widget.Material3.Button.TextButton.Dialog"
android:layout_width="wrap_content"
- android:layout_height="36dp"
+ android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginTop="24dp"
android:layout_marginEnd="8dp"
@@ -103,6 +103,7 @@
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
+ android:minHeight="36dp"
android:text="@string/all_close"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/homeworkDialogAdd"
@@ -112,13 +113,14 @@
android:id="@+id/homeworkDialogAdd"
style="@style/Widget.Material3.Button.TextButton.Dialog"
android:layout_width="wrap_content"
- android:layout_height="36dp"
+ android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
android:insetLeft="0dp"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
+ android:minHeight="36dp"
android:text="@string/all_add"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/layout/dialog_lesson_completed.xml b/app/src/main/res/layout/dialog_lesson_completed.xml
index 3a1d3fd0..fc32a252 100644
--- a/app/src/main/res/layout/dialog_lesson_completed.xml
+++ b/app/src/main/res/layout/dialog_lesson_completed.xml
@@ -212,7 +212,7 @@
android:id="@+id/completedLessonDialogClose"
style="@style/Widget.Material3.Button.TextButton.Dialog"
android:layout_width="wrap_content"
- android:layout_height="36dp"
+ android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="24dp"
@@ -220,6 +220,7 @@
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
+ android:minHeight="36dp"
android:text="@string/all_close"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/layout/dialog_mobile_device.xml b/app/src/main/res/layout/dialog_mobile_device.xml
index 9b81737f..c526ed74 100644
--- a/app/src/main/res/layout/dialog_mobile_device.xml
+++ b/app/src/main/res/layout/dialog_mobile_device.xml
@@ -18,10 +18,10 @@
android:layout_marginTop="24dp"
android:adjustViewBounds="true"
android:contentDescription="@string/mobile_device_qr"
- tools:src="@tools:sample/avatars"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
+ app:layout_constraintTop_toTopOf="parent"
+ tools:src="@tools:sample/avatars" />
+
+ app:constraint_referenced_ids="mobileDeviceQr,mobileDeviceDialogTokenTitle,mobileDeviceDialogTokenValue,mobileDeviceDialogSymbolTitle,mobileDeviceDialogSymbolValue,mobileDeviceDialogPinTitle,mobileDeviceDialogPinValue,mobileDeviceDialogClose"
+ tools:visibility="visible" />
+ tools:visibility="invisible" />
diff --git a/app/src/main/res/layout/dialog_note.xml b/app/src/main/res/layout/dialog_note.xml
index 9c8b18b3..3b88ea5f 100644
--- a/app/src/main/res/layout/dialog_note.xml
+++ b/app/src/main/res/layout/dialog_note.xml
@@ -180,7 +180,7 @@
android:id="@+id/noteDialogClose"
style="@style/Widget.Material3.Button.TextButton.Dialog"
android:layout_width="wrap_content"
- android:layout_height="36dp"
+ android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="24dp"
@@ -188,6 +188,7 @@
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
+ android:minHeight="36dp"
android:text="@string/all_close"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/layout/dialog_school_announcement.xml b/app/src/main/res/layout/dialog_school_announcement.xml
index 4e0ef556..a771b772 100644
--- a/app/src/main/res/layout/dialog_school_announcement.xml
+++ b/app/src/main/res/layout/dialog_school_announcement.xml
@@ -122,7 +122,7 @@
android:id="@+id/announcementDialogClose"
style="@style/Widget.Material3.Button.TextButton.Dialog"
android:layout_width="wrap_content"
- android:layout_height="36dp"
+ android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="24dp"
@@ -130,6 +130,7 @@
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
+ android:minHeight="36dp"
android:text="@string/all_close"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/layout/dialog_timetable.xml b/app/src/main/res/layout/dialog_timetable.xml
index aeb01b3b..de269648 100644
--- a/app/src/main/res/layout/dialog_timetable.xml
+++ b/app/src/main/res/layout/dialog_timetable.xml
@@ -263,7 +263,7 @@
android:id="@+id/timetableDialogClose"
style="@style/Widget.Material3.Button.TextButton.Dialog"
android:layout_width="wrap_content"
- android:layout_height="36dp"
+ android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="24dp"
@@ -271,6 +271,7 @@
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
+ android:minHeight="36dp"
android:text="@string/all_close"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/layout/header_grade_details.xml b/app/src/main/res/layout/header_grade_details.xml
index f2ba9a8c..e43e8993 100644
--- a/app/src/main/res/layout/header_grade_details.xml
+++ b/app/src/main/res/layout/header_grade_details.xml
@@ -45,6 +45,9 @@
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/gradeHeaderPointsSum"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="@id/gradeHeaderSubject"
app:layout_constraintTop_toBottomOf="@+id/gradeHeaderSubject"
tools:text="Average: 6,00" />
@@ -55,8 +58,12 @@
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
+ android:ellipsize="end"
+ android:maxLines="1"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintEnd_toStartOf="@id/gradeHeaderNumber"
app:layout_constraintStart_toEndOf="@+id/gradeHeaderAverage"
app:layout_constraintTop_toBottomOf="@+id/gradeHeaderSubject"
tools:text="Points: 123/200 (61,5%)" />
@@ -67,8 +74,13 @@
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="5dp"
+ android:layout_marginEnd="8dp"
+ android:ellipsize="end"
+ android:maxLines="1"
android:textColor="?android:textColorSecondary"
android:textSize="12sp"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/gradeHeaderPointsSum"
app:layout_constraintTop_toBottomOf="@id/gradeHeaderSubject"
tools:text="12 grades" />
@@ -85,6 +97,9 @@
android:paddingRight="5dp"
android:textColor="?colorOnPrimary"
android:textSize="14sp"
+ app:autoSizeMaxTextSize="16dp"
+ app:autoSizeMinTextSize="10dp"
+ app:autoSizeTextType="uniform"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
diff --git a/app/src/main/res/layout/item_timetable.xml b/app/src/main/res/layout/item_timetable.xml
index 57af6f7e..b9966c12 100644
--- a/app/src/main/res/layout/item_timetable.xml
+++ b/app/src/main/res/layout/item_timetable.xml
@@ -1,7 +1,6 @@
@@ -49,8 +49,9 @@
android:maxLines="1"
android:textColor="?android:textColorSecondary"
android:textSize="13sp"
+ app:layout_constraintBottom_toTopOf="@id/timetableItemTimeFinish"
app:layout_constraintStart_toEndOf="@id/timetableItemNumber"
- app:layout_constraintTop_toTopOf="@id/timetableItemNumber"
+ app:layout_constraintTop_toTopOf="parent"
tools:text="11:11" />
@@ -83,13 +91,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
- android:layout_marginTop="0dp"
- android:layout_marginEnd="5dp"
+ android:layout_marginEnd="0dp"
+ android:ellipsize="end"
+ android:maxLines="1"
android:textColor="?android:textColorSecondary"
android:textSize="13sp"
app:layout_constraintEnd_toStartOf="@+id/timetableItemTeacher"
app:layout_constraintStart_toEndOf="@+id/timetableItemRoom"
- app:layout_constraintTop_toTopOf="@+id/timetableItemTimeFinish"
+ app:layout_constraintTop_toTopOf="@id/timetableItemTimeFinish"
tools:text="(2/2)"
tools:visibility="visible" />
@@ -98,13 +107,15 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
- android:layout_marginEnd="16dp"
+ android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="?android:textColorSecondary"
android:textSize="13sp"
- app:layout_constraintBottom_toBottomOf="@+id/timetableItemNumber"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/timetableItemGroup"
+ app:layout_constraintTop_toTopOf="@id/timetableItemTimeFinish"
tools:text="Agata Kowalska - Błaszczyk"
tools:visibility="visible" />
diff --git a/app/src/main/res/layout/subitem_dashboard_small_grade.xml b/app/src/main/res/layout/subitem_dashboard_small_grade.xml
index 6800b72e..3684c267 100644
--- a/app/src/main/res/layout/subitem_dashboard_small_grade.xml
+++ b/app/src/main/res/layout/subitem_dashboard_small_grade.xml
@@ -1,5 +1,6 @@
From e378b4c70adc8b4e4be7f302c51a16c9377066cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sun, 25 Feb 2024 16:36:50 +0100
Subject: [PATCH 02/25] Fix loading timetable and attendance when should be
refreshed returns true (#2436)
---
.../wulkanowy/data/db/dao/TimetableDao.kt | 2 +-
.../data/repositories/AttendanceRepository.kt | 10 +++------
.../data/repositories/TimetableRepository.kt | 22 ++++++++++++++-----
.../IsStudentHasLessonsOnWeekendUseCase.kt | 11 ++--------
.../services/sync/works/TimetableWork.kt | 4 +---
.../modules/attendance/AttendancePresenter.kt | 18 ++++++---------
.../modules/dashboard/DashboardPresenter.kt | 2 +-
.../modules/timetable/TimetablePresenter.kt | 7 +++---
8 files changed, 34 insertions(+), 42 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt
index b4b7379f..40d97ea9 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/TimetableDao.kt
@@ -15,5 +15,5 @@ interface TimetableDao : BaseDao {
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow>
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
- fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List
+ suspend fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
index 6d782047..bbf627de 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
@@ -16,10 +16,8 @@ import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.switchSemester
import io.github.wulkanowy.utils.uniqueSubtract
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.withContext
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
@@ -58,11 +56,9 @@ class AttendanceRepository @Inject constructor(
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
},
fetch = {
- val lessons = withContext(Dispatchers.IO) {
- timetableDb.load(
- semester.diaryId, semester.studentId, start.monday, end.sunday
- )
- }
+ val lessons = timetableDb.load(
+ semester.diaryId, semester.studentId, start.monday, end.sunday
+ )
sdk.init(student)
.switchSemester(semester)
.getAttendance(start.monday, end.sunday)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
index 9305d3b3..acbd02d1 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
@@ -3,13 +3,23 @@ package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
-import io.github.wulkanowy.data.db.entities.*
+import io.github.wulkanowy.data.db.entities.Semester
+import io.github.wulkanowy.data.db.entities.Student
+import io.github.wulkanowy.data.db.entities.Timetable
+import io.github.wulkanowy.data.db.entities.TimetableAdditional
+import io.github.wulkanowy.data.db.entities.TimetableHeader
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.data.pojos.TimetableFull
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
-import io.github.wulkanowy.utils.*
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.monday
+import io.github.wulkanowy.utils.sunday
+import io.github.wulkanowy.utils.switchSemester
+import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.sync.Mutex
@@ -121,12 +131,12 @@ class TimetableRepository @Inject constructor(
}
}
- fun getTimetableFromDatabase(
+ suspend fun getTimetableFromDatabase(
semester: Semester,
- from: LocalDate,
+ start: LocalDate,
end: LocalDate
- ): Flow> {
- return timetableDb.loadAll(semester.diaryId, semester.studentId, from, end)
+ ): List {
+ return timetableDb.load(semester.diaryId, semester.studentId, start, end)
}
suspend fun updateTimetable(timetable: List) {
diff --git a/app/src/main/java/io/github/wulkanowy/domain/timetable/IsStudentHasLessonsOnWeekendUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/timetable/IsStudentHasLessonsOnWeekendUseCase.kt
index efe928e2..ffd00574 100644
--- a/app/src/main/java/io/github/wulkanowy/domain/timetable/IsStudentHasLessonsOnWeekendUseCase.kt
+++ b/app/src/main/java/io/github/wulkanowy/domain/timetable/IsStudentHasLessonsOnWeekendUseCase.kt
@@ -1,10 +1,7 @@
package io.github.wulkanowy.domain.timetable
-import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.TimetableRepository
-import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.sunday
import java.time.LocalDate
@@ -16,18 +13,14 @@ class IsStudentHasLessonsOnWeekendUseCase @Inject constructor(
) {
suspend operator fun invoke(
- student: Student,
semester: Semester,
currentDate: LocalDate = LocalDate.now(),
): Boolean {
- val lessons = timetableRepository.getTimetable(
- student = student,
+ val lessons = timetableRepository.getTimetableFromDatabase(
semester = semester,
start = currentDate.monday,
end = currentDate.sunday,
- forceRefresh = false,
- timetableType = TimetableRepository.TimetableType.NORMAL
- ).toFirstResult().dataOrNull?.lessons.orEmpty()
+ )
return isWeekendHasLessonsUseCase(lessons)
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt
index ac9a8eb4..2d10d925 100644
--- a/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt
+++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/TimetableWork.kt
@@ -6,7 +6,6 @@ import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification
import io.github.wulkanowy.utils.nextOrSameSchoolDay
-import kotlinx.coroutines.flow.first
import java.time.LocalDate.now
import javax.inject.Inject
@@ -31,10 +30,9 @@ class TimetableWork @Inject constructor(
timetableRepository.getTimetableFromDatabase(
semester = semester,
- from = startDate,
+ start = startDate,
end = endDate,
)
- .first()
.filterNot { it.isNotified }
.let {
if (it.isNotEmpty()) changeTimetableNotification.notify(it, student)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt
index f66479da..82fe69cb 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt
@@ -4,18 +4,14 @@ import android.annotation.SuppressLint
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.db.entities.Student
-import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.repositories.AttendanceRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
-import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.*
-import kotlinx.coroutines.flow.catch
-import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.time.DayOfWeek
@@ -210,7 +206,7 @@ class AttendancePresenter @Inject constructor(
val semester = semesterRepository.getCurrentSemester(student)
- checkInitialAndCurrentDate(student, semester)
+ checkInitialAndCurrentDate(semester)
attendanceRepository.getAttendance(
student = student,
semester = semester,
@@ -266,15 +262,13 @@ class AttendancePresenter @Inject constructor(
.launch()
}
- private suspend fun checkInitialAndCurrentDate(student: Student, semester: Semester) {
+ private suspend fun checkInitialAndCurrentDate(semester: Semester) {
if (initialDate == null) {
- val lessons = attendanceRepository.getAttendance(
- student = student,
+ val lessons = attendanceRepository.getAttendanceFromDatabase(
semester = semester,
start = now().monday,
end = now().sunday,
- forceRefresh = false,
- ).toFirstResult().dataOrNull.orEmpty()
+ ).firstOrNull().orEmpty()
isWeekendHasLessons = isWeekendHasLessons(lessons)
initialDate = getInitialDate(semester)
}
@@ -316,6 +310,7 @@ class AttendancePresenter @Inject constructor(
showContent(false)
showExcuseButton(false)
}
+
is Resource.Success -> {
Timber.i("Excusing for absence result: Success")
analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size)
@@ -328,6 +323,7 @@ class AttendancePresenter @Inject constructor(
}
loadData(forceRefresh = true)
}
+
is Resource.Error -> {
Timber.i("Excusing for absence result: An exception occurred")
errorHandler.dispatch(it.error)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index 1e6f1c19..784ac112 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -438,7 +438,7 @@ class DashboardPresenter @Inject constructor(
private fun loadLessons(student: Student, forceRefresh: Boolean) {
flatResourceFlow {
val semester = semesterRepository.getCurrentSemester(student)
- val date = when (isStudentHasLessonsOnWeekendUseCase(student, semester)) {
+ val date = when (isStudentHasLessonsOnWeekendUseCase(semester)) {
true -> LocalDate.now()
else -> LocalDate.now().nextOrSameSchoolDay
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt
index 7e8c876e..e83f2517 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt
@@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.timetable
import android.os.Handler
import android.os.Looper
import io.github.wulkanowy.data.db.entities.Semester
-import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.enums.TimetableGapsMode.BETWEEN_AND_BEFORE_LESSONS
import io.github.wulkanowy.data.enums.TimetableGapsMode.NO_GAPS
@@ -150,7 +149,7 @@ class TimetablePresenter @Inject constructor(
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
- checkInitialAndCurrentDate(student, semester)
+ checkInitialAndCurrentDate(semester)
timetableRepository.getTimetable(
student = student,
semester = semester,
@@ -194,9 +193,9 @@ class TimetablePresenter @Inject constructor(
.launch()
}
- private suspend fun checkInitialAndCurrentDate(student: Student, semester: Semester) {
+ private suspend fun checkInitialAndCurrentDate(semester: Semester) {
if (initialDate == null) {
- isWeekendHasLessons = isStudentHasLessonsOnWeekendUseCase(student, semester)
+ isWeekendHasLessons = isStudentHasLessonsOnWeekendUseCase(semester)
initialDate = getInitialDate(semester)
}
From d5c17285c1ce29c87e3f28cf380b8691d7bb468a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sun, 25 Feb 2024 16:37:28 +0100
Subject: [PATCH 03/25] Fix error handling in login (#2437)
---
app/build.gradle | 2 +-
.../main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt | 6 +++++-
.../io/github/wulkanowy/ui/modules/auth/AuthPresenter.kt | 6 +++++-
.../wulkanowy/ui/modules/login/form/LoginFormPresenter.kt | 4 ++++
.../java/io/github/wulkanowy/utils/ExceptionExtension.kt | 2 ++
app/src/main/res/values/api_hosts.xml | 2 +-
app/src/main/res/values/strings.xml | 1 +
7 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 26c2547e..b8123667 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -195,7 +195,7 @@ ext {
}
dependencies {
- implementation 'io.github.wulkanowy:sdk:2.4.1'
+ implementation 'io.github.wulkanowy:sdk:2.4.2-SNAPSHOT'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt
index e17c0c9e..7109f1ff 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/ErrorHandler.kt
@@ -34,7 +34,7 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
}
protected open fun proceed(error: Throwable) {
- showErrorMessage(context.resources.getErrorString(error), error)
+ showDefaultMessage(error)
when (error) {
is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl)
is ScramblerException -> onDecryptionFailed()
@@ -45,6 +45,10 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
}
}
+ fun showDefaultMessage(error: Throwable) {
+ showErrorMessage(context.resources.getErrorString(error), error)
+ }
+
open fun clear() {
showErrorMessage = { _, _ -> }
onExpiredCredentials = {}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthPresenter.kt
index 8f579712..3c061f49 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthPresenter.kt
@@ -62,7 +62,11 @@ class AuthPresenter @Inject constructor(
}
isSuccess
}
- .onFailure { errorHandler.dispatch(it) }
+ .onFailure {
+ errorHandler.dispatch(it)
+ view?.showProgress(false)
+ view?.showContent(true)
+ }
.onSuccess {
if (it) {
view?.showSuccess(true)
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 69e1d027..39bc3f02 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
@@ -14,6 +14,7 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase
+import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
@@ -204,6 +205,9 @@ class LoginFormPresenter @Inject constructor(
}
.onResourceError {
loginErrorHandler.dispatch(it)
+ if (it is InvalidSymbolException) {
+ loginErrorHandler.showDefaultMessage(it)
+ }
lastError = it
view?.showContact(true)
analytics.logEvent(
diff --git a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
index 18fc10bb..1c229051 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
@@ -3,6 +3,7 @@ package io.github.wulkanowy.utils
import android.content.res.Resources
import io.github.wulkanowy.R
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
+import io.github.wulkanowy.sdk.scrapper.exception.AccountInactiveException
import io.github.wulkanowy.sdk.scrapper.exception.CloudflareVerificationException
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException
@@ -33,6 +34,7 @@ fun Resources.getErrorString(error: Throwable): String = when (error) {
is ServiceUnavailableException -> R.string.error_service_unavailable
is FeatureDisabledException -> R.string.error_feature_disabled
is FeatureNotAvailableException -> R.string.error_feature_not_available
+ is AccountInactiveException -> R.string.error_account_inactive
is VulcanException -> R.string.error_unknown_uonet
is ScrapperException -> R.string.error_unknown_app
is CloudflareVerificationException -> R.string.error_cloudflare_captcha
diff --git a/app/src/main/res/values/api_hosts.xml b/app/src/main/res/values/api_hosts.xml
index 6439b462..9768329d 100644
--- a/app/src/main/res/values/api_hosts.xml
+++ b/app/src/main/res/values/api_hosts.xml
@@ -66,7 +66,7 @@
- gminaulanmajorat
- gminaozorkow
- gminalopiennikgorny
- - warszawa
+ - saas1
- powiatwulkanowy
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0a4dcf7f..faed4d18 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -852,6 +852,7 @@
No internet connection
An error occurred. Check your device clock
+ This account is inactive. Try logging in again
Connection to register failed. Servers can be overloaded. Please try again later
Loading data failed. Please try again later
Register password change required
From 74a20b2f65cb7af7be333fba86990f3961d94643 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Tue, 27 Feb 2024 09:42:44 +0100
Subject: [PATCH 04/25] Add Github Sponsor (#2444)
---
.github/FUNDING.yml | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 .github/FUNDING.yml
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..cdce0759
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,4 @@
+# These are supported funding model platforms
+
+github: wulkanowy
+custom: https://www.paypal.com/paypalme/wulkanowy
From 1b8c3899842505a4f1616fac8ce7b550afb602ad Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 27 Feb 2024 08:52:47 +0000
Subject: [PATCH 05/25] Bump io.coil-kt:coil from 2.5.0 to 2.6.0 (#2441)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index b8123667..e88d9205 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -246,7 +246,7 @@ dependencies {
implementation 'com.github.Faierbel:slf4j-timber:2.0'
implementation 'com.github.bastienpaulfr:Treessence:1.1.2'
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
- implementation 'io.coil-kt:coil:2.5.0'
+ implementation 'io.coil-kt:coil:2.6.0'
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
From 1ab300d74f4ea41c395f4db1d7b8f926e363b3c6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 27 Feb 2024 08:53:00 +0000
Subject: [PATCH 06/25] Bump android_hilt from 1.1.0 to 1.2.0 (#2443)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index e88d9205..07efeb2f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -187,7 +187,7 @@ huaweiPublish {
ext {
work_manager = "2.9.0"
- android_hilt = "1.1.0"
+ android_hilt = "1.2.0"
room = "2.6.1"
chucker = "4.0.0"
mockk = "1.13.9"
From 7a4032dda4e3061a09ecffd69712b3598e82e414 Mon Sep 17 00:00:00 2001
From: JestemKamil <84380834+JestemKamil@users.noreply.github.com>
Date: Thu, 29 Feb 2024 21:30:02 +0100
Subject: [PATCH 07/25] Add mute message senders (#2415)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Mikołaj Pich
---
.../60.json | 2527 +++++++++++++++++
app/src/main/assets/contributors.json | 4 +
.../io/github/wulkanowy/data/DataModule.kt | 4 +
.../github/wulkanowy/data/db/AppDatabase.kt | 8 +-
.../wulkanowy/data/db/dao/MessagesDao.kt | 10 +-
.../data/db/dao/MutedMessageSendersDao.kt | 20 +
.../data/db/entities/MessageWithAttachment.kt | 8 +-
.../db/entities/MessageWithMutedAuthor.kt | 12 +
.../data/db/entities/MutedMessageSender.kt | 15 +
.../data/repositories/MessageRepository.kt | 35 +-
.../modules/dashboard/DashboardPresenter.kt | 1 +
.../message/preview/MessagePreviewAdapter.kt | 6 +
.../message/preview/MessagePreviewFragment.kt | 18 +-
.../preview/MessagePreviewPresenter.kt | 106 +-
.../message/preview/MessagePreviewView.kt | 6 +
.../modules/message/tab/MessageTabAdapter.kt | 16 +-
.../modules/message/tab/MessageTabDataItem.kt | 1 +
.../message/tab/MessageTabPresenter.kt | 38 +-
.../res/drawable/ic_circle_notification.xml | 10 +
.../res/drawable/ic_notifications_off.xml | 5 +
app/src/main/res/layout/item_message.xml | 6 +-
.../res/menu/action_menu_message_preview.xml | 7 +
app/src/main/res/values/strings.xml | 6 +
.../repositories/MessageRepositoryTest.kt | 47 +-
24 files changed, 2827 insertions(+), 89 deletions(-)
create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/60.json
create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/dao/MutedMessageSendersDao.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithMutedAuthor.kt
create mode 100644 app/src/main/java/io/github/wulkanowy/data/db/entities/MutedMessageSender.kt
create mode 100644 app/src/main/res/drawable/ic_circle_notification.xml
create mode 100644 app/src/main/res/drawable/ic_notifications_off.xml
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/60.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/60.json
new file mode 100644
index 00000000..20eacad1
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/60.json
@@ -0,0 +1,2527 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 60,
+ "identityHash": "3672d3f4d5e6b874e5a22d2bb458dc65",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `scrapper_domain_suffix` TEXT NOT NULL DEFAULT '', `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "scrapperDomainSuffix",
+ "columnName": "scrapper_domain_suffix",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_Students_email_symbol_student_id_school_id_class_id",
+ "unique": true,
+ "columnNames": [
+ "email",
+ "symbol",
+ "student_id",
+ "school_id",
+ "class_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Semesters",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "kindergartenDiaryId",
+ "columnName": "kindergarten_diary_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "kindergarten_diary_id",
+ "semester_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timeId",
+ "columnName": "time_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presence",
+ "columnName": "presence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "exemption",
+ "columnName": "exemption",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lateness",
+ "columnName": "lateness",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excused",
+ "columnName": "excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deleted",
+ "columnName": "deleted",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excusable",
+ "columnName": "excusable",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excuseStatus",
+ "columnName": "excuse_status",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectId",
+ "columnName": "subject_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "month",
+ "columnName": "month",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presence",
+ "columnName": "presence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absenceExcused",
+ "columnName": "absence_excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absenceForSchoolReasons",
+ "columnName": "absence_for_school_reasons",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lateness",
+ "columnName": "lateness",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "latenessExcused",
+ "columnName": "lateness_excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "exemption",
+ "columnName": "exemption",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entry",
+ "columnName": "entry",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "value",
+ "columnName": "value",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "modifier",
+ "columnName": "modifier",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "comment",
+ "columnName": "comment",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "color",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gradeSymbol",
+ "columnName": "grade_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "weight",
+ "columnName": "weight",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "weightValue",
+ "columnName": "weightValue",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isRead",
+ "columnName": "is_read",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "position",
+ "columnName": "position",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGrade",
+ "columnName": "predicted_grade",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGrade",
+ "columnName": "final_grade",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "proposedPoints",
+ "columnName": "proposed_points",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalPoints",
+ "columnName": "final_points",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "pointsSum",
+ "columnName": "points_sum",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "average",
+ "columnName": "average",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "others",
+ "columnName": "others",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "student",
+ "columnName": "student",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mailboxKey",
+ "columnName": "mailbox_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondents",
+ "columnName": "correspondents",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "recipients",
+ "columnName": "recipients",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`message_global_key`, `url`, `filename`))",
+ "fields": [
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "message_global_key",
+ "url",
+ "filename"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isRead",
+ "columnName": "is_read",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "luckyNumber",
+ "columnName": "lucky_number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "topic",
+ "columnName": "topic",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "substitution",
+ "columnName": "substitution",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "resources",
+ "columnName": "resources",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Mailboxes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))",
+ "fields": [
+ {
+ "fieldPath": "globalKey",
+ "columnName": "globalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolId",
+ "columnName": "schoolId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "studentName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolNameShort",
+ "columnName": "schoolNameShort",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "globalKey"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "mailboxGlobalKey",
+ "columnName": "mailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentMailboxGlobalKey",
+ "columnName": "studentMailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "schoolShortName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceId",
+ "columnName": "device_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shortName",
+ "columnName": "short_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "contact",
+ "columnName": "contact",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "headmaster",
+ "columnName": "headmaster",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "pedagogue",
+ "columnName": "pedagogue",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "destination",
+ "columnName": "destination",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'"
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `types` TEXT NOT NULL DEFAULT '[]', `is_ok_visible` INTEGER NOT NULL DEFAULT 0, `is_x_visible` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "types",
+ "columnName": "types",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'[]'"
+ },
+ {
+ "fieldPath": "isOkVisible",
+ "columnName": "is_ok_visible",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "isXVisible",
+ "columnName": "is_x_visible",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MutedMessageSenders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`author` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "author",
+ "columnName": "author",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesDescriptive",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `description` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "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, '3672d3f4d5e6b874e5a22d2bb458dc65')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/assets/contributors.json b/app/src/main/assets/contributors.json
index a7629c22..97ac9356 100644
--- a/app/src/main/assets/contributors.json
+++ b/app/src/main/assets/contributors.json
@@ -54,5 +54,9 @@
{
"displayName": "Antoni Paduch",
"githubUsername": "janAte1"
+ },
+ {
+ "displayName": "Kamil Wąsik",
+ "githubUsername": "JestemKamil"
}
]
diff --git a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt
index 7c9cf9a3..6b6c9d32 100644
--- a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt
@@ -254,6 +254,10 @@ internal class DataModule {
@Provides
fun provideAdminMessageDao(database: AppDatabase) = database.adminMessagesDao
+ @Singleton
+ @Provides
+ fun provideMutesDao(database: AppDatabase) = database.mutedMessageSendersDao
+
@Singleton
@Provides
fun provideGradeDescriptiveDao(database: AppDatabase) = database.gradeDescriptiveDao
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 8e5841fe..21a6e3f3 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
@@ -25,6 +25,7 @@ import io.github.wulkanowy.data.db.dao.MailboxDao
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
import io.github.wulkanowy.data.db.dao.MessagesDao
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
+import io.github.wulkanowy.data.db.dao.MutedMessageSendersDao
import io.github.wulkanowy.data.db.dao.NoteDao
import io.github.wulkanowy.data.db.dao.NotificationDao
import io.github.wulkanowy.data.db.dao.RecipientDao
@@ -56,6 +57,7 @@ import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.db.entities.MobileDevice
+import io.github.wulkanowy.data.db.entities.MutedMessageSender
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Notification
import io.github.wulkanowy.data.db.entities.Recipient
@@ -157,6 +159,7 @@ import javax.inject.Singleton
SchoolAnnouncement::class,
Notification::class,
AdminMessage::class,
+ MutedMessageSender::class,
GradeDescriptive::class,
],
autoMigrations = [
@@ -169,6 +172,7 @@ import javax.inject.Singleton
AutoMigration(from = 56, to = 57, spec = Migration57::class),
AutoMigration(from = 57, to = 58, spec = Migration58::class),
AutoMigration(from = 58, to = 59),
+ AutoMigration(from = 59, to = 60),
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@@ -177,7 +181,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
- const val VERSION_SCHEMA = 59
+ const val VERSION_SCHEMA = 60
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
@@ -303,5 +307,7 @@ abstract class AppDatabase : RoomDatabase() {
abstract val adminMessagesDao: AdminMessageDao
+ abstract val mutedMessageSendersDao: MutedMessageSendersDao
+
abstract val gradeDescriptiveDao: GradeDescriptiveDao
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt
index 1709f763..11e6da1e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MessagesDao.kt
@@ -5,15 +5,23 @@ import androidx.room.Query
import androidx.room.Transaction
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
+import io.github.wulkanowy.data.db.entities.MessageWithMutedAuthor
import kotlinx.coroutines.flow.Flow
@Dao
interface MessagesDao : BaseDao {
-
@Transaction
@Query("SELECT * FROM Messages WHERE message_global_key = :messageGlobalKey")
fun loadMessageWithAttachment(messageGlobalKey: String): Flow
+ @Transaction
+ @Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
+ fun loadMessagesWithMutedAuthor(mailboxKey: String, folder: Int): Flow>
+
+ @Transaction
+ @Query("SELECT * FROM Messages WHERE email = :email AND folder_id = :folder ORDER BY date DESC")
+ fun loadMessagesWithMutedAuthor(folder: Int, email: String): Flow>
+
@Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
fun loadAll(mailboxKey: String, folder: Int): Flow>
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/MutedMessageSendersDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/MutedMessageSendersDao.kt
new file mode 100644
index 00000000..0a866401
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/MutedMessageSendersDao.kt
@@ -0,0 +1,20 @@
+package io.github.wulkanowy.data.db.dao
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import io.github.wulkanowy.data.db.entities.MutedMessageSender
+
+@Dao
+interface MutedMessageSendersDao : BaseDao {
+
+ @Query("SELECT COUNT(*) FROM MutedMessageSenders WHERE author = :author")
+ suspend fun checkMute(author: String): Boolean
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ suspend fun insertMute(mute: MutedMessageSender): Long
+
+ @Query("DELETE FROM MutedMessageSenders WHERE author = :author")
+ suspend fun deleteMute(author: String)
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt
index cd468215..fc890e76 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithAttachment.kt
@@ -2,11 +2,15 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.Embedded
import androidx.room.Relation
+import java.io.Serializable
data class MessageWithAttachment(
@Embedded
val message: Message,
@Relation(parentColumn = "message_global_key", entityColumn = "message_global_key")
- val attachments: List
-)
+ val attachments: List,
+
+ @Relation(parentColumn = "correspondents", entityColumn = "author")
+ val mutedMessageSender: MutedMessageSender?,
+) : Serializable
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithMutedAuthor.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithMutedAuthor.kt
new file mode 100644
index 00000000..e3cd1ca7
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MessageWithMutedAuthor.kt
@@ -0,0 +1,12 @@
+package io.github.wulkanowy.data.db.entities
+
+import androidx.room.Embedded
+import androidx.room.Relation
+
+data class MessageWithMutedAuthor(
+ @Embedded
+ val message: Message,
+
+ @Relation(parentColumn = "correspondents", entityColumn = "author")
+ val mutedMessageSender: MutedMessageSender?,
+)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/MutedMessageSender.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/MutedMessageSender.kt
new file mode 100644
index 00000000..f1770e64
--- /dev/null
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/MutedMessageSender.kt
@@ -0,0 +1,15 @@
+package io.github.wulkanowy.data.db.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import java.io.Serializable
+
+@Entity(tableName = "MutedMessageSenders")
+data class MutedMessageSender(
+ @ColumnInfo(name = "author")
+ val author: String,
+) : Serializable {
+ @PrimaryKey(autoGenerate = true)
+ var id: Long = 0
+}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
index c8fccb23..6d591c5b 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
@@ -8,9 +8,12 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.dao.MailboxDao
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
import io.github.wulkanowy.data.db.dao.MessagesDao
+import io.github.wulkanowy.data.db.dao.MutedMessageSendersDao
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
+import io.github.wulkanowy.data.db.entities.MessageWithMutedAuthor
+import io.github.wulkanowy.data.db.entities.MutedMessageSender
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder
@@ -42,6 +45,7 @@ import javax.inject.Singleton
@Singleton
class MessageRepository @Inject constructor(
private val messagesDb: MessagesDao,
+ private val mutedMessageSendersDao: MutedMessageSendersDao,
private val messageAttachmentDao: MessageAttachmentDao,
private val sdk: Sdk,
@ApplicationContext private val context: Context,
@@ -51,7 +55,6 @@ class MessageRepository @Inject constructor(
private val mailboxDao: MailboxDao,
private val getMailboxByStudentUseCase: GetMailboxByStudentUseCase,
) {
-
private val saveFetchResultMutex = Mutex()
private val messagesCacheKey = "message"
@@ -63,7 +66,7 @@ class MessageRepository @Inject constructor(
folder: MessageFolder,
forceRefresh: Boolean,
notify: Boolean = false,
- ): Flow>> = networkBoundResource(
+ ): Flow>> = networkBoundResource(
mutex = saveFetchResultMutex,
isResultEmpty = { it.isEmpty() },
shouldFetch = {
@@ -74,8 +77,8 @@ class MessageRepository @Inject constructor(
},
query = {
if (mailbox == null) {
- messagesDb.loadAll(folder.id, student.email)
- } else messagesDb.loadAll(mailbox.globalKey, folder.id)
+ messagesDb.loadMessagesWithMutedAuthor(folder.id, student.email)
+ } else messagesDb.loadMessagesWithMutedAuthor(mailbox.globalKey, folder.id)
},
fetch = {
sdk.init(student).getMessages(
@@ -83,10 +86,12 @@ class MessageRepository @Inject constructor(
mailboxKey = mailbox?.globalKey,
).mapToEntities(student, mailbox, mailboxDao.loadAll(student.email))
},
- saveFetchResult = { old, new ->
+ saveFetchResult = { oldWithAuthors, new ->
+ val old = oldWithAuthors.map { it.message }
messagesDb.deleteAll(old uniqueSubtract new)
messagesDb.insertAll((new uniqueSubtract old).onEach {
- it.isNotified = !notify
+ val muted = isMuted(it.correspondents)
+ it.isNotified = !notify || muted
})
refreshHelper.updateLastRefreshTimestamp(
@@ -106,9 +111,7 @@ class MessageRepository @Inject constructor(
Timber.d("Message content in db empty: ${it.message.content.isBlank()}")
(it.message.unread && markAsRead) || it.message.content.isBlank()
},
- query = {
- messagesDb.loadMessageWithAttachment(message.messageGlobalKey)
- },
+ query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) },
fetch = {
sdk.init(student).getMessageDetails(
messageKey = it!!.message.messageGlobalKey,
@@ -236,4 +239,18 @@ class MessageRepository @Inject constructor(
context.getString(R.string.pref_key_message_draft),
value?.let { json.encodeToString(it) }
)
+
+ suspend fun isMuted(author: String): Boolean {
+ return mutedMessageSendersDao.checkMute(author)
+ }
+
+ suspend fun muteMessage(author: String) {
+ if (isMuted(author)) return
+ mutedMessageSendersDao.insertMute(MutedMessageSender(author))
+ }
+
+ suspend fun unmuteMessage(author: String) {
+ if (!isMuted(author)) return
+ mutedMessageSendersDao.deleteMute(author)
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
index 784ac112..3fec6256 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/dashboard/DashboardPresenter.kt
@@ -304,6 +304,7 @@ class DashboardPresenter @Inject constructor(
forceRefresh = forceRefresh
)
}
+ .mapResourceData { it.map { messageWithAuthor -> messageWithAuthor.message } }
.onResourceError { errorHandler.dispatch(it) }
.takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt
index d3c6b95c..b83f7e23 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewAdapter.kt
@@ -50,12 +50,15 @@ class MessagePreviewAdapter @Inject constructor() :
ViewType.MESSAGE.id -> MessageViewHolder(
ItemMessagePreviewBinding.inflate(inflater, parent, false)
)
+
ViewType.DIVIDER.id -> DividerViewHolder(
ItemMessageDividerBinding.inflate(inflater, parent, false)
)
+
ViewType.ATTACHMENT.id -> AttachmentViewHolder(
ItemMessageAttachmentBinding.inflate(inflater, parent, false)
)
+
else -> throw IllegalStateException()
}
}
@@ -66,6 +69,7 @@ class MessagePreviewAdapter @Inject constructor() :
holder,
requireNotNull(messageWithAttachment).message
)
+
is AttachmentViewHolder -> bindAttachment(
holder,
requireNotNull(messageWithAttachment).attachments[position - 2]
@@ -82,9 +86,11 @@ class MessagePreviewAdapter @Inject constructor() :
recipientCount > 1 -> {
context.getString(R.string.message_read_by, message.readBy, recipientCount)
}
+
message.readBy == 1 || (isReceived && !message.unread) -> {
context.getString(R.string.message_read, context.getString(R.string.all_yes))
}
+
else -> context.getString(R.string.message_read, context.getString(R.string.all_no))
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt
index 3ed685cd..3b33bb51 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt
@@ -50,12 +50,20 @@ class MessagePreviewFragment :
private var menuPrintButton: MenuItem? = null
+ private var menuMuteButton: MenuItem? = null
+
override val titleStringId: Int
get() = R.string.message_title
override val deleteMessageSuccessString: String
get() = getString(R.string.message_delete_success)
+ override val muteMessageSuccessString: String
+ get() = getString(R.string.message_mute_success)
+
+ override val unmuteMessageSuccessString: String
+ get() = getString(R.string.message_unmute_success)
+
override val messageNoSubjectString: String
get() = getString(R.string.message_no_subject)
@@ -106,6 +114,7 @@ class MessagePreviewFragment :
menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete)
menuShareButton = menu.findItem(R.id.messagePreviewMenuShare)
menuPrintButton = menu.findItem(R.id.messagePreviewMenuPrint)
+ menuMuteButton = menu.findItem(R.id.messagePreviewMenuMute)
presenter.onCreateOptionsMenu()
menu.findItem(R.id.mainMenuAccount).isVisible = false
@@ -118,6 +127,7 @@ class MessagePreviewFragment :
R.id.messagePreviewMenuDelete -> presenter.onMessageDelete()
R.id.messagePreviewMenuShare -> presenter.onShare()
R.id.messagePreviewMenuPrint -> presenter.onPrint()
+ R.id.messagePreviewMenuMute -> presenter.onMute()
else -> false
}
}
@@ -129,6 +139,11 @@ class MessagePreviewFragment :
}
}
+ override fun updateMuteToggleButton(isMuted: Boolean) {
+ menuMuteButton?.setTitle(if (isMuted) R.string.message_unmute else R.string.message_mute)
+
+ }
+
override fun showProgress(show: Boolean) {
binding.messagePreviewProgress.visibility = if (show) VISIBLE else GONE
}
@@ -143,6 +158,7 @@ class MessagePreviewFragment :
menuDeleteButton?.isVisible = show
menuShareButton?.isVisible = show
menuPrintButton?.isVisible = show
+ menuMuteButton?.isVisible = show && isReplayable
}
override fun setDeletedOptionsLabels() {
@@ -213,7 +229,7 @@ class MessagePreviewFragment :
}
override fun onSaveInstanceState(outState: Bundle) {
- outState.putSerializable(MESSAGE_ID_KEY, presenter.message)
+ outState.putSerializable(MESSAGE_ID_KEY, presenter.messageWithAttachments)
super.onSaveInstanceState(outState)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt
index cd7b7284..2eff245f 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt
@@ -5,7 +5,7 @@ import androidx.core.text.parseAsHtml
import io.github.wulkanowy.R
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Message
-import io.github.wulkanowy.data.db.entities.MessageAttachment
+import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
@@ -26,9 +26,7 @@ class MessagePreviewPresenter @Inject constructor(
private val analytics: AnalyticsHelper
) : BasePresenter(errorHandler, studentRepository) {
- var message: Message? = null
-
- var attachments: List? = null
+ var messageWithAttachments: MessageWithAttachment? = null
private lateinit var lastError: Throwable
@@ -38,7 +36,6 @@ class MessagePreviewPresenter @Inject constructor(
super.onAttachView(view)
view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError
- this.message = message
loadData(requireNotNull(message))
}
@@ -66,13 +63,12 @@ class MessagePreviewPresenter @Inject constructor(
.logResourceStatus("message ${messageToLoad.messageId} preview")
.onResourceData {
if (it != null) {
- message = it.message
- attachments = it.attachments
+ messageWithAttachments = it
view?.apply {
setMessageWithAttachment(it)
showContent(true)
initOptions()
-
+ updateMuteToggleButton(isMuted = it.mutedMessageSender != null)
if (preferencesRepository.isIncognitoMode && it.message.unread) {
showMessage(R.string.message_incognito_description)
}
@@ -83,8 +79,7 @@ class MessagePreviewPresenter @Inject constructor(
popView()
}
}
- }
- .onResourceSuccess {
+ }.onResourceSuccess {
if (it != null) {
analytics.logEvent(
"load_item",
@@ -92,31 +87,28 @@ class MessagePreviewPresenter @Inject constructor(
"length" to it.message.content.length
)
}
- }
- .onResourceNotLoading { view?.showProgress(false) }
- .onResourceError {
+ }.onResourceNotLoading { view?.showProgress(false) }.onResourceError {
retryCallback = { onMessageLoadRetry(messageToLoad) }
errorHandler.dispatch(it)
- }
- .launch()
+ }.launch()
}
fun onReply(): Boolean {
- return if (message != null) {
- view?.openMessageReply(message)
+ return if (messageWithAttachments?.message != null) {
+ view?.openMessageReply(messageWithAttachments?.message)
true
} else false
}
fun onForward(): Boolean {
- return if (message != null) {
- view?.openMessageForward(message)
+ return if (messageWithAttachments?.message != null) {
+ view?.openMessageForward(messageWithAttachments?.message)
true
} else false
}
fun onShare(): Boolean {
- val message = message ?: return false
+ val message = messageWithAttachments?.message ?: return false
val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
val text = buildString {
@@ -129,13 +121,15 @@ class MessagePreviewPresenter @Inject constructor(
appendLine(message.content.parseAsHtml())
- if (!attachments.isNullOrEmpty()) {
+ if (!messageWithAttachments?.attachments.isNullOrEmpty()) {
appendLine()
appendLine("Załączniki:")
- append(attachments.orEmpty().joinToString(separator = "\n") { attachment ->
- "${attachment.filename}: ${attachment.url}"
- })
+ append(
+ messageWithAttachments?.attachments.orEmpty()
+ .joinToString(separator = "\n") { attachment ->
+ "${attachment.filename}: ${attachment.url}"
+ })
}
}
@@ -148,7 +142,7 @@ class MessagePreviewPresenter @Inject constructor(
@SuppressLint("NewApi")
fun onPrint(): Boolean {
- val message = message ?: return false
+ val message = messageWithAttachments?.message ?: return false
val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
val dateString = message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
@@ -159,8 +153,7 @@ class MessagePreviewPresenter @Inject constructor(
append("Od
${message.sender}
")
append("DO
${message.recipients}")
}
- val messageContent = "${message.content}
"
- .replace(Regex("[\\n\\r]{2,}"), "")
+ val messageContent = "
${message.content}
".replace(Regex("[\\n\\r]{2,}"), "")
.replace(Regex("[\\n\\r]"), "
")
val jobName = buildString {
@@ -171,9 +164,7 @@ class MessagePreviewPresenter @Inject constructor(
}
view?.apply {
- val html = printHTML
- .replace("%SUBJECT%", subject)
- .replace("%CONTENT%", messageContent)
+ val html = printHTML.replace("%SUBJECT%", subject).replace("%CONTENT%", messageContent)
.replace("%INFO%", infoContent)
printDocument(html, jobName)
}
@@ -182,7 +173,7 @@ class MessagePreviewPresenter @Inject constructor(
}
private fun deleteMessage() {
- message ?: return
+ messageWithAttachments?.message ?: return
view?.run {
showContent(false)
@@ -191,24 +182,22 @@ class MessagePreviewPresenter @Inject constructor(
showErrorView(false)
}
- Timber.i("Delete message ${message?.messageGlobalKey}")
+ Timber.i("Delete message ${messageWithAttachments?.message?.messageGlobalKey}")
presenterScope.launch {
runCatching {
val student = studentRepository.getCurrentStudent(decryptPass = true)
val mailbox = messageRepository.getMailboxByStudent(student)
- messageRepository.deleteMessage(student, mailbox, message!!)
+ messageRepository.deleteMessage(student, mailbox, messageWithAttachments?.message!!)
+ }.onFailure {
+ retryCallback = { onMessageDelete() }
+ errorHandler.dispatch(it)
+ }.onSuccess {
+ view?.run {
+ showMessage(deleteMessageSuccessString)
+ popView()
+ }
}
- .onFailure {
- retryCallback = { onMessageDelete() }
- errorHandler.dispatch(it)
- }
- .onSuccess {
- view?.run {
- showMessage(deleteMessageSuccessString)
- popView()
- }
- }
view?.showProgress(false)
}
@@ -232,10 +221,10 @@ class MessagePreviewPresenter @Inject constructor(
private fun initOptions() {
view?.apply {
showOptions(
- show = message != null,
- isReplayable = message?.folderId != MessageFolder.SENT.id,
+ show = messageWithAttachments?.message != null,
+ isReplayable = messageWithAttachments?.message?.folderId != MessageFolder.SENT.id,
)
- message?.let {
+ messageWithAttachments?.message?.let {
when (it.folderId == MessageFolder.TRASHED.id) {
true -> setDeletedOptionsLabels()
false -> setNotDeletedOptionsLabels()
@@ -248,4 +237,29 @@ class MessagePreviewPresenter @Inject constructor(
fun onCreateOptionsMenu() {
initOptions()
}
+
+ fun onMute(): Boolean {
+ val message = messageWithAttachments?.message ?: return false
+ val isMuted = messageWithAttachments?.mutedMessageSender != null
+
+ presenterScope.launch {
+ runCatching {
+ when (isMuted) {
+ true -> {
+ messageRepository.unmuteMessage(message.correspondents)
+ view?.run { showMessage(unmuteMessageSuccessString) }
+ }
+
+ false -> {
+ messageRepository.muteMessage(message.correspondents)
+ view?.run { showMessage(muteMessageSuccessString) }
+ }
+ }
+ }.onFailure {
+ errorHandler.dispatch(it)
+ }
+ }
+ view?.updateMuteToggleButton(isMuted)
+ return true
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt
index 7f5f140b..cbe1c3cb 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt
@@ -9,6 +9,10 @@ interface MessagePreviewView : BaseView {
val deleteMessageSuccessString: String
+ val muteMessageSuccessString: String
+
+ val unmuteMessageSuccessString: String
+
val messageNoSubjectString: String
val printHTML: String
@@ -19,6 +23,8 @@ interface MessagePreviewView : BaseView {
fun setMessageWithAttachment(item: MessageWithAttachment)
+ fun updateMuteToggleButton(isMuted: Boolean)
+
fun showProgress(show: Boolean)
fun showContent(show: Boolean)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt
index 9792c708..fadc77e6 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabAdapter.kt
@@ -18,8 +18,7 @@ import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.toFormattedString
import javax.inject.Inject
-class MessageTabAdapter @Inject constructor() :
- RecyclerView.Adapter() {
+class MessageTabAdapter @Inject constructor() : RecyclerView.Adapter() {
lateinit var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit
@@ -52,10 +51,11 @@ class MessageTabAdapter @Inject constructor() :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
- return when (MessageItemViewType.values()[viewType]) {
+ return when (MessageItemViewType.entries[viewType]) {
MessageItemViewType.FILTERS -> HeaderViewHolder(
ItemMessageChipsBinding.inflate(inflater, parent, false)
)
+
MessageItemViewType.MESSAGE -> ItemViewHolder(
ItemMessageBinding.inflate(inflater, parent, false)
)
@@ -137,7 +137,12 @@ class MessageTabAdapter @Inject constructor() :
ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(currentTextColor))
isVisible = message.hasAttachments
}
- messageItemUnreadIndicator.isVisible = message.unread
+ messageItemUnreadIndicator.isVisible = message.unread || item.isMuted
+
+ when (item.isMuted) {
+ true -> messageItemUnreadIndicator.setImageResource(R.drawable.ic_notifications_off)
+ else -> messageItemUnreadIndicator.setImageResource(R.drawable.ic_circle_notification)
+ }
root.setOnClickListener {
holder.bindingAdapterPosition.let {
@@ -165,8 +170,7 @@ class MessageTabAdapter @Inject constructor() :
RecyclerView.ViewHolder(binding.root)
private class MessageTabDiffUtil(
- private val old: List,
- private val new: List
+ private val old: List, private val new: List
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = old.size
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt
index c0bd4170..ef640e04 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabDataItem.kt
@@ -6,6 +6,7 @@ sealed class MessageTabDataItem(val viewType: MessageItemViewType) {
data class MessageItem(
val message: Message,
+ val isMuted: Boolean,
val isSelected: Boolean,
val isActionMode: Boolean
) : MessageTabDataItem(MessageItemViewType.MESSAGE)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt
index 90f93b14..f8283721 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt
@@ -4,6 +4,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.Message
+import io.github.wulkanowy.data.db.entities.MessageWithMutedAuthor
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.repositories.MessageRepository
import io.github.wulkanowy.data.repositories.StudentRepository
@@ -39,7 +40,7 @@ class MessageTabPresenter @Inject constructor(
private var mailboxes: List = emptyList()
private var selectedMailbox: Mailbox? = null
- private var messages = emptyList()
+ private var messages = emptyList()
private val searchChannel = Channel()
@@ -141,7 +142,7 @@ class MessageTabPresenter @Inject constructor(
}
fun onActionModeSelectCheckAll() {
- val messagesToSelect = getFilteredData()
+ val messagesToSelect = getFilteredData().map { it.message }
val isAllSelected = messagesToDelete.containsAll(messagesToSelect)
if (isAllSelected) {
@@ -188,7 +189,7 @@ class MessageTabPresenter @Inject constructor(
view?.showActionMode(false)
}
- val filteredData = getFilteredData()
+ val filteredData = getFilteredData().map { it.message }
view?.run {
updateActionModeTitle(messagesToDelete.size)
@@ -320,25 +321,31 @@ class MessageTabPresenter @Inject constructor(
}
}
- private fun getFilteredData(): List {
+ private fun getFilteredData(): List {
if (lastSearchQuery.trim().isEmpty()) {
- val sortedMessages = messages.sortedByDescending { it.date }
+ val sortedMessages = messages.sortedByDescending { it.message.date }
return when {
- (onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
- (onlyUnread == true) -> sortedMessages.filter { it.unread == onlyUnread }
- onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments }
+ (onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter {
+ it.message.unread == onlyUnread && it.message.hasAttachments == onlyWithAttachments
+ }
+
+ (onlyUnread == true) -> sortedMessages.filter { it.message.unread == onlyUnread }
+ onlyWithAttachments -> sortedMessages.filter { it.message.hasAttachments == onlyWithAttachments }
else -> sortedMessages
}
} else {
val sortedMessages = messages
- .map { it to calculateMatchRatio(it, lastSearchQuery) }
- .sortedWith(compareBy> { -it.second }.thenByDescending { it.first.date })
+ .map { it to calculateMatchRatio(it.message, lastSearchQuery) }
+ .sortedWith(compareBy> { -it.second }.thenByDescending { it.first.message.date })
.filter { it.second > 6000 }
.map { it.first }
return when {
- (onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
- (onlyUnread == true) -> sortedMessages.filter { it.unread == onlyUnread }
- onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments }
+ (onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter {
+ it.message.unread == onlyUnread && it.message.hasAttachments == onlyWithAttachments
+ }
+
+ (onlyUnread == true) -> sortedMessages.filter { it.message.unread == onlyUnread }
+ onlyWithAttachments -> sortedMessages.filter { it.message.hasAttachments == onlyWithAttachments }
else -> sortedMessages
}
}
@@ -367,8 +374,9 @@ class MessageTabPresenter @Inject constructor(
addAll(data.map { message ->
MessageTabDataItem.MessageItem(
- message = message,
- isSelected = messagesToDelete.any { it.messageGlobalKey == message.messageGlobalKey },
+ message = message.message,
+ isMuted = message.mutedMessageSender != null,
+ isSelected = messagesToDelete.any { it.messageGlobalKey == message.message.messageGlobalKey },
isActionMode = isActionMode
)
})
diff --git a/app/src/main/res/drawable/ic_circle_notification.xml b/app/src/main/res/drawable/ic_circle_notification.xml
new file mode 100644
index 00000000..6059212c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_circle_notification.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_notifications_off.xml b/app/src/main/res/drawable/ic_notifications_off.xml
new file mode 100644
index 00000000..094ed75f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_notifications_off.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/item_message.xml b/app/src/main/res/layout/item_message.xml
index 39fbaad0..1346c3f0 100644
--- a/app/src/main/res/layout/item_message.xml
+++ b/app/src/main/res/layout/item_message.xml
@@ -81,9 +81,9 @@
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index faed4d18..5bb06a41 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -864,4 +864,10 @@
Feature disabled by your school
Feature not available. Login in a mode other than Mobile API
This field is required
+
+
+ Mute
+ Unmute
+ You have muted this user
+ You have unmuted this user
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
index 3a18ee97..58937e77 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
@@ -6,8 +6,10 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.dao.MailboxDao
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
import io.github.wulkanowy.data.db.dao.MessagesDao
+import io.github.wulkanowy.data.db.dao.MutedMessageSendersDao
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
+import io.github.wulkanowy.data.db.entities.MutedMessageSender
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.errorOrNull
import io.github.wulkanowy.data.toFirstResult
@@ -19,9 +21,16 @@ import io.github.wulkanowy.sdk.pojo.Folder
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.Status
import io.github.wulkanowy.utils.status
-import io.mockk.*
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.checkEquals
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
+import io.mockk.just
+import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toList
@@ -45,6 +54,9 @@ class MessageRepositoryTest {
@MockK
private lateinit var messageDb: MessagesDao
+ @MockK
+ private lateinit var mutesDb: MutedMessageSendersDao
+
@MockK
private lateinit var messageAttachmentDao: MessageAttachmentDao
@@ -73,9 +85,22 @@ class MessageRepositoryTest {
fun setUp() {
MockKAnnotations.init(this)
every { refreshHelper.shouldBeRefreshed(any()) } returns false
-
+ coEvery { mutesDb.checkMute(any()) } returns false
+ coEvery {
+ messageDb.loadMessagesWithMutedAuthor(
+ mailboxKey = any(),
+ folder = any()
+ )
+ } returns flowOf(emptyList())
+ coEvery {
+ messageDb.loadMessagesWithMutedAuthor(
+ folder = any(),
+ email = any()
+ )
+ } returns flowOf(emptyList())
repository = MessageRepository(
messagesDb = messageDb,
+ mutedMessageSendersDao = mutesDb,
messageAttachmentDao = messageAttachmentDao,
sdk = sdk,
context = context,
@@ -131,7 +156,11 @@ class MessageRepositoryTest {
@Test
fun `get message when content already in db`() {
val testMessage = getMessageEntity(123, "Test", false)
- val messageWithAttachment = MessageWithAttachment(testMessage, emptyList())
+ val messageWithAttachment = MessageWithAttachment(
+ testMessage,
+ emptyList(),
+ MutedMessageSender("Jan Kowalski - P - (WULKANOWY)")
+ )
coEvery { messageDb.loadMessageWithAttachment("v4") } returns flowOf(
messageWithAttachment
@@ -149,8 +178,16 @@ class MessageRepositoryTest {
val testMessage = getMessageEntity(123, "", true)
val testMessageWithContent = testMessage.copy().apply { content = "Test" }
- val mWa = MessageWithAttachment(testMessage, emptyList())
- val mWaWithContent = MessageWithAttachment(testMessageWithContent, emptyList())
+ val mWa = MessageWithAttachment(
+ testMessage,
+ emptyList(),
+ MutedMessageSender("Jan Kowalski - P - (WULKANOWY)")
+ )
+ val mWaWithContent = MessageWithAttachment(
+ testMessageWithContent,
+ emptyList(),
+ MutedMessageSender("Jan Kowalski - P - (WULKANOWY)")
+ )
coEvery {
messageDb.loadMessageWithAttachment("v4")
From 2c1337bb518893397e04b3ae99384e20c564e6c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Thu, 29 Feb 2024 21:36:51 +0100
Subject: [PATCH 08/25] New Crowdin updates (#2439)
---
app/src/main/res/values-cs/strings.xml | 1 +
app/src/main/res/values-de/strings.xml | 1 +
app/src/main/res/values-pl/strings.xml | 1 +
app/src/main/res/values-ru/strings.xml | 1 +
app/src/main/res/values-sk/strings.xml | 1 +
app/src/main/res/values-uk/strings.xml | 1 +
6 files changed, 6 insertions(+)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index e1cafa6e..2e0104b1 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -854,6 +854,7 @@
Žádné internetové připojení
Vyskytla se chyba. Zkontrolujte hodiny svého zařízení
+ This account is inactive. Try logging in again
Nelze se připojit ke deníku. Servery mohou být přetíženy. Prosím zkuste to znovu později
Načítání dat se nezdařilo. Prosím zkuste to znovu později
Je vyžadována změna hesla pro deník
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 5bd71bb2..b04558aa 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -760,6 +760,7 @@
Keine Internetverbindung
Es ist ein Fehler aufgetreten. Überprüfen Sie Ihre Geräteuhr
+ This account is inactive. Try logging in again
Registrierungsverbindung fehlgeschlagen. Server können überlastet sein. Bitte versuchen Sie es später noch einmal
Das Laden der Daten ist fehlgeschlagen. Bitte versuchen Sie es später noch einmal
Passwortänderung für Registrierung erforderlich
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 70d4982b..9a7ee3f8 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -854,6 +854,7 @@
Brak połączenia z internetem
Wystąpił błąd. Sprawdź poprawność daty w urządzeniu
+ Konto jest nieaktywne. Spróbuj zalogować się ponownie
Nie udało się połączyć z dziennikiem. Serwery mogą być przeciążone. Spróbuj ponownie później
Ładowanie danych nie powiodło się. Spróbuj ponownie później
Wymagana zmiana hasła do dziennika
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 717e0213..b7786546 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -854,6 +854,7 @@
Интернет-соединение отсутствует
Произошла ошибка. Проверьте время на вашем устройстве
+ This account is inactive. Try logging in again
Не удалось подключиться к дневнику. Возможно, сервера перегружены, повторите попытку позже
Не удалось загрузить данные, повторите попытку позже
Необходимо изменить пароль дневника
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 368ead9d..d34302ec 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -854,6 +854,7 @@
Žiadne internetové pripojenie
Vyskytla sa chyba. Skontrolujte hodiny svojho zariadenia
+ This account is inactive. Try logging in again
Nedá sa pripojiť ku denníku. Servery môžu byť preťažené. Prosím skúste to znova neskôr
Načítanie údajov zlyhalo. Skúste neskôr prosím
Je vyžadovaná zmena hesla pre denník
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 3d10f117..228b87d4 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -854,6 +854,7 @@
Немає з\'єднання з інтернетом
Сталася помилка. Перевірте годинник пристрою
+ This account is inactive. Try logging in again
Помилка підключення до щоденнику. Сервери можуть бути перевантажені, спробуйте пізніше
Помилка завантаження даних, спробуйте пізніше
Необхідна зміна пароля щоденника
From c198e6a2f7e55910f96142524c1ae03139d1e368 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Fri, 1 Mar 2024 00:06:54 +0100
Subject: [PATCH 09/25] New Crowdin updates (#2445)
---
app/src/main/res/values-cs/strings.xml | 5 +++++
app/src/main/res/values-de/strings.xml | 5 +++++
app/src/main/res/values-pl/strings.xml | 5 +++++
app/src/main/res/values-ru/strings.xml | 5 +++++
app/src/main/res/values-sk/strings.xml | 5 +++++
app/src/main/res/values-uk/strings.xml | 5 +++++
6 files changed, 30 insertions(+)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 2e0104b1..5c4c52da 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -866,4 +866,9 @@
Funkce je deaktivována přes vaší školou
Funkce není k dispozici. Přihlaste se v jiném režimu než Mobile API
Toto pole je povinné
+
+ Mute
+ Unmute
+ You have muted this user
+ You have unmuted this user
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index b04558aa..a346bbd2 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -772,4 +772,9 @@
Funktion, die von Ihrer Schule deaktiviert wurde
Feature in diesem Modus nicht verfügbar
Dieses Feld ist erforderlich
+
+ Mute
+ Unmute
+ You have muted this user
+ You have unmuted this user
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 9a7ee3f8..56a85ea2 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -866,4 +866,9 @@
Funkcja wyłączona przez szkołę
Funkcja niedostępna. Zaloguj się w trybie innym niż Mobilne API
To pole jest wymagane
+
+ Wycisz
+ Wyłącz wyciszenie
+ Wyciszyleś tego użytkownika
+ Wyłączyłeś wyciszenie tego użytkownika
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index b7786546..f7469675 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -866,4 +866,9 @@
Функция отключена вашей школой
Функция недоступна в режиме Mobile API. Воспользуйтесь другим режимом
Это поле обязательно
+
+ Mute
+ Unmute
+ You have muted this user
+ You have unmuted this user
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index d34302ec..56238c10 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -866,4 +866,9 @@
Funkcia je deaktivovaná cez vašou školou
Funkcia nie je k dispozícii. Prihláste sa v inom režime než Mobile API
Toto pole je povinné
+
+ Mute
+ Unmute
+ You have muted this user
+ You have unmuted this user
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 228b87d4..a8202747 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -866,4 +866,9 @@
Функція вимкнена вашою школою
Функція недоступна в режимі Mobile API. Увійдіть в інший режим
Це поле обовʼязкове
+
+ Mute
+ Unmute
+ You have muted this user
+ You have unmuted this user
From c04752ed39e847009bc4ab1997a4aef43a545ca0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 1 Mar 2024 10:32:55 +0100
Subject: [PATCH 10/25] Fix timetable items layout (#2446)
---
app/src/main/res/layout/item_timetable.xml | 11 +++++++----
.../main/res/layout/subitem_dashboard_small_grade.xml | 1 +
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/app/src/main/res/layout/item_timetable.xml b/app/src/main/res/layout/item_timetable.xml
index b9966c12..d1310522 100644
--- a/app/src/main/res/layout/item_timetable.xml
+++ b/app/src/main/res/layout/item_timetable.xml
@@ -24,6 +24,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0"
tools:text="5" />
@@ -179,7 +182,7 @@
android:visibility="gone"
app:backgroundTint="?colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintTop_toTopOf="@id/timetableItemTimeStart"
tools:text="jeszcze 15 min"
tools:visibility="visible" />
diff --git a/app/src/main/res/layout/subitem_dashboard_small_grade.xml b/app/src/main/res/layout/subitem_dashboard_small_grade.xml
index 3684c267..5d48313a 100644
--- a/app/src/main/res/layout/subitem_dashboard_small_grade.xml
+++ b/app/src/main/res/layout/subitem_dashboard_small_grade.xml
@@ -11,6 +11,7 @@
android:gravity="center"
android:maxLength="5"
android:minWidth="20dp"
+ android:padding="1dp"
android:textColor="@android:color/white"
android:textSize="12sp"
android:textStyle="bold"
From ea28fc783cf2c24e25606f5ba13dff120d1101ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Fri, 1 Mar 2024 21:14:43 +0100
Subject: [PATCH 11/25] Add message from trash restoring (#2438)
---
.../wulkanowy/data/enums/MessageFolder.kt | 7 ++-
.../data/repositories/MessageRepository.kt | 50 ++++++++++-----
.../message/preview/MessagePreviewFragment.kt | 27 ++++----
.../preview/MessagePreviewPresenter.kt | 61 +++++++++++++++----
.../message/preview/MessagePreviewView.kt | 8 +--
.../message/send/SendMessagePresenter.kt | 2 +-
.../modules/message/tab/MessageTabFragment.kt | 16 +++--
.../message/tab/MessageTabPresenter.kt | 23 ++++++-
.../ic_menu_message_delete_forever.xml | 9 +++
.../res/drawable/ic_menu_message_restore.xml | 9 +++
.../res/menu/action_menu_message_preview.xml | 14 +++++
.../res/menu/context_menu_message_tab.xml | 14 +++++
app/src/main/res/values/strings.xml | 3 +
13 files changed, 192 insertions(+), 51 deletions(-)
create mode 100644 app/src/main/res/drawable/ic_menu_message_delete_forever.xml
create mode 100644 app/src/main/res/drawable/ic_menu_message_restore.xml
diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/MessageFolder.kt b/app/src/main/java/io/github/wulkanowy/data/enums/MessageFolder.kt
index 899ba908..7cb4202a 100644
--- a/app/src/main/java/io/github/wulkanowy/data/enums/MessageFolder.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/enums/MessageFolder.kt
@@ -3,5 +3,10 @@ package io.github.wulkanowy.data.enums
enum class MessageFolder(val id: Int = 1) {
RECEIVED(1),
SENT(2),
- TRASHED(3)
+ TRASHED(3),
+ ;
+
+ companion object {
+ fun byId(id: Int) = entries.first { it.id == id }
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
index 6d591c5b..96f04870 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
@@ -18,6 +18,7 @@ import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
+import io.github.wulkanowy.data.enums.MessageFolder.SENT
import io.github.wulkanowy.data.enums.MessageFolder.TRASHED
import io.github.wulkanowy.data.mappers.mapFromEntities
import io.github.wulkanowy.data.mappers.mapToEntities
@@ -25,6 +26,7 @@ import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.pojos.MessageDraft
+import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase
import io.github.wulkanowy.sdk.Sdk
@@ -34,7 +36,6 @@ import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@@ -155,17 +156,30 @@ class MessageRepository @Inject constructor(
subject: String,
content: String,
recipients: List,
- mailboxId: String,
+ mailbox: Mailbox,
) {
sdk.init(student).sendMessage(
subject = subject,
content = content,
recipients = recipients.mapFromEntities(),
- mailboxId = mailboxId,
+ mailboxId = mailbox.globalKey,
)
+ refreshFolders(student, mailbox, listOf(SENT))
}
- suspend fun deleteMessages(student: Student, mailbox: Mailbox?, messages: List) {
+ suspend fun restoreMessages(student: Student, mailbox: Mailbox?, messages: List) {
+ sdk.init(student).restoreMessages(
+ messages = messages.map { it.messageGlobalKey },
+ )
+
+ refreshFolders(student, mailbox)
+ }
+
+ suspend fun deleteMessage(student: Student, message: Message) {
+ deleteMessages(student, listOf(message))
+ }
+
+ suspend fun deleteMessages(student: Student, messages: List) {
val firstMessage = messages.first()
sdk.init(student).deleteMessages(
messages = messages.map { it.messageGlobalKey },
@@ -184,18 +198,24 @@ class MessageRepository @Inject constructor(
}
messagesDb.updateAll(deletedMessages)
- } else messagesDb.deleteAll(messages)
-
- getMessages(
- student = student,
- mailbox = mailbox,
- folder = TRASHED,
- forceRefresh = true,
- ).first()
+ } else {
+ messagesDb.deleteAll(messages)
+ }
}
- suspend fun deleteMessage(student: Student, mailbox: Mailbox?, message: Message) {
- deleteMessages(student, mailbox, listOf(message))
+ private suspend fun refreshFolders(
+ student: Student,
+ mailbox: Mailbox?,
+ folders: List = MessageFolder.entries
+ ) {
+ folders.forEach {
+ getMessages(
+ student = student,
+ mailbox = mailbox,
+ folder = it,
+ forceRefresh = true,
+ ).toFirstResult()
+ }
}
suspend fun getMailboxes(student: Student, forceRefresh: Boolean) = networkBoundResource(
@@ -240,7 +260,7 @@ class MessageRepository @Inject constructor(
value?.let { json.encodeToString(it) }
)
- suspend fun isMuted(author: String): Boolean {
+ private suspend fun isMuted(author: String): Boolean {
return mutedMessageSendersDao.checkMute(author)
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt
index 3b33bb51..75778bac 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt
@@ -44,8 +44,12 @@ class MessagePreviewFragment :
private var menuForwardButton: MenuItem? = null
+ private var menuRestoreButton: MenuItem? = null
+
private var menuDeleteButton: MenuItem? = null
+ private var menuDeleteForeverButton: MenuItem? = null
+
private var menuShareButton: MenuItem? = null
private var menuPrintButton: MenuItem? = null
@@ -64,6 +68,9 @@ class MessagePreviewFragment :
override val unmuteMessageSuccessString: String
get() = getString(R.string.message_unmute_success)
+ override val restoreMessageSuccessString: String
+ get() = getString(R.string.message_restore_success)
+
override val messageNoSubjectString: String
get() = getString(R.string.message_no_subject)
@@ -111,7 +118,9 @@ class MessagePreviewFragment :
inflater.inflate(R.menu.action_menu_message_preview, menu)
menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply)
menuForwardButton = menu.findItem(R.id.messagePreviewMenuForward)
+ menuRestoreButton = menu.findItem(R.id.messagePreviewMenuRestore)
menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete)
+ menuDeleteForeverButton = menu.findItem(R.id.messagePreviewMenuDeleteForever)
menuShareButton = menu.findItem(R.id.messagePreviewMenuShare)
menuPrintButton = menu.findItem(R.id.messagePreviewMenuPrint)
menuMuteButton = menu.findItem(R.id.messagePreviewMenuMute)
@@ -124,7 +133,9 @@ class MessagePreviewFragment :
return when (item.itemId) {
R.id.messagePreviewMenuReply -> presenter.onReply()
R.id.messagePreviewMenuForward -> presenter.onForward()
+ R.id.messagePreviewMenuRestore -> presenter.onMessageRestore()
R.id.messagePreviewMenuDelete -> presenter.onMessageDelete()
+ R.id.messagePreviewMenuDeleteForever -> presenter.onMessageDelete()
R.id.messagePreviewMenuShare -> presenter.onShare()
R.id.messagePreviewMenuPrint -> presenter.onPrint()
R.id.messagePreviewMenuMute -> presenter.onMute()
@@ -152,23 +163,17 @@ class MessagePreviewFragment :
binding.messagePreviewRecycler.visibility = if (show) VISIBLE else GONE
}
- override fun showOptions(show: Boolean, isReplayable: Boolean) {
- menuReplyButton?.isVisible = isReplayable
+ override fun showOptions(show: Boolean, isReplayable: Boolean, isRestorable: Boolean) {
+ menuReplyButton?.isVisible = show && isReplayable
menuForwardButton?.isVisible = show
- menuDeleteButton?.isVisible = show
+ menuRestoreButton?.isVisible = show && isRestorable
+ menuDeleteButton?.isVisible = show && !isRestorable
+ menuDeleteForeverButton?.isVisible = show && isRestorable
menuShareButton?.isVisible = show
menuPrintButton?.isVisible = show
menuMuteButton?.isVisible = show && isReplayable
}
- override fun setDeletedOptionsLabels() {
- menuDeleteButton?.setTitle(R.string.message_delete_forever)
- }
-
- override fun setNotDeletedOptionsLabels() {
- menuDeleteButton?.setTitle(R.string.message_move_to_trash)
- }
-
override fun showErrorView(show: Boolean) {
binding.messagePreviewError.visibility = if (show) VISIBLE else GONE
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt
index 2eff245f..9bb0d32a 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt
@@ -14,9 +14,11 @@ 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.toFormattedString
+import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
class MessagePreviewPresenter @Inject constructor(
errorHandler: ErrorHandler,
@@ -74,6 +76,7 @@ class MessagePreviewPresenter @Inject constructor(
}
}
} else {
+ delay(1.seconds)
view?.run {
showMessage(messageNotExists)
popView()
@@ -172,13 +175,51 @@ class MessagePreviewPresenter @Inject constructor(
return true
}
+ private fun restoreMessage() {
+ val message = messageWithAttachments?.message ?: return
+
+ view?.run {
+ showContent(false)
+ showProgress(true)
+ showOptions(
+ show = false,
+ isReplayable = false,
+ isRestorable = false,
+ )
+ showErrorView(false)
+ }
+ Timber.i("Restore message ${message.messageGlobalKey}")
+ presenterScope.launch {
+ runCatching {
+ val student = studentRepository.getCurrentStudent(decryptPass = true)
+ val mailbox = messageRepository.getMailboxByStudent(student)
+ messageRepository.restoreMessages(student, mailbox, listOfNotNull(message))
+ }
+ .onFailure {
+ retryCallback = { onMessageRestore() }
+ errorHandler.dispatch(it)
+ }
+ .onSuccess {
+ view?.run {
+ showMessage(restoreMessageSuccessString)
+ popView()
+ }
+ }
+ view?.showProgress(false)
+ }
+ }
+
private fun deleteMessage() {
messageWithAttachments?.message ?: return
view?.run {
showContent(false)
showProgress(true)
- showOptions(show = false, isReplayable = false)
+ showOptions(
+ show = false,
+ isReplayable = false,
+ isRestorable = false,
+ )
showErrorView(false)
}
@@ -187,8 +228,7 @@ class MessagePreviewPresenter @Inject constructor(
presenterScope.launch {
runCatching {
val student = studentRepository.getCurrentStudent(decryptPass = true)
- val mailbox = messageRepository.getMailboxByStudent(student)
- messageRepository.deleteMessage(student, mailbox, messageWithAttachments?.message!!)
+ messageRepository.deleteMessage(student, messageWithAttachments?.message!!)
}.onFailure {
retryCallback = { onMessageDelete() }
errorHandler.dispatch(it)
@@ -213,6 +253,11 @@ class MessagePreviewPresenter @Inject constructor(
}
}
+ fun onMessageRestore(): Boolean {
+ restoreMessage()
+ return true
+ }
+
fun onMessageDelete(): Boolean {
deleteMessage()
return true
@@ -222,15 +267,9 @@ class MessagePreviewPresenter @Inject constructor(
view?.apply {
showOptions(
show = messageWithAttachments?.message != null,
- isReplayable = messageWithAttachments?.message?.folderId != MessageFolder.SENT.id,
+ isReplayable = messageWithAttachments?.message?.folderId == MessageFolder.RECEIVED.id,
+ isRestorable = messageWithAttachments?.message?.folderId == MessageFolder.TRASHED.id,
)
- messageWithAttachments?.message?.let {
- when (it.folderId == MessageFolder.TRASHED.id) {
- true -> setDeletedOptionsLabels()
- false -> setNotDeletedOptionsLabels()
- }
- }
-
}
}
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt
index cbe1c3cb..ee0b6ce0 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewView.kt
@@ -13,6 +13,8 @@ interface MessagePreviewView : BaseView {
val unmuteMessageSuccessString: String
+ val restoreMessageSuccessString: String
+
val messageNoSubjectString: String
val printHTML: String
@@ -35,11 +37,7 @@ interface MessagePreviewView : BaseView {
fun setErrorRetryCallback(callback: () -> Unit)
- fun showOptions(show: Boolean, isReplayable: Boolean)
-
- fun setDeletedOptionsLabels()
-
- fun setNotDeletedOptionsLabels()
+ fun showOptions(show: Boolean, isReplayable: Boolean, isRestorable: Boolean)
fun openMessageReply(message: Message?)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt
index e776e994..6155baea 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/send/SendMessagePresenter.kt
@@ -203,7 +203,7 @@ class SendMessagePresenter @Inject constructor(
subject = subject,
content = content,
recipients = recipients,
- mailboxId = mailbox.globalKey,
+ mailbox = mailbox,
)
}.logResourceStatus("sending message").onEach {
when (it) {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt
index 4364e868..12f9d323 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabFragment.kt
@@ -5,7 +5,9 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
-import android.view.View.*
+import android.view.View.GONE
+import android.view.View.INVISIBLE
+import android.view.View.VISIBLE
import android.widget.CompoundButton
import androidx.annotation.StringRes
import androidx.appcompat.view.ActionMode
@@ -64,10 +66,12 @@ class MessageTabFragment : BaseFragment(R.layout.frag
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
- if (presenter.folder == MessageFolder.TRASHED) {
- val menuItem = menu.findItem(R.id.messageTabContextMenuDelete)
- menuItem.setTitle(R.string.message_delete_forever)
- }
+ val isTrashFolder = presenter.folder == MessageFolder.TRASHED
+
+ menu.findItem(R.id.messageTabContextMenuDelete).setVisible(!isTrashFolder)
+ menu.findItem(R.id.messageTabContextMenuDeleteForever).setVisible(isTrashFolder)
+ menu.findItem(R.id.messageTabContextMenuRestore).setVisible(isTrashFolder)
+
return presenter.onPrepareActionMode()
}
@@ -79,6 +83,8 @@ class MessageTabFragment : BaseFragment(R.layout.frag
override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean {
when (menu.itemId) {
R.id.messageTabContextMenuDelete -> presenter.onActionModeSelectDelete()
+ R.id.messageTabContextMenuRestore -> presenter.onActionModeSelectRestore()
+ R.id.messageTabContextMenuDeleteForever -> presenter.onActionModeSelectDelete()
R.id.messageTabContextMenuSelectAll -> presenter.onActionModeSelectCheckAll()
}
return true
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt
index f8283721..cda0b32b 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt
@@ -121,8 +121,27 @@ class MessageTabPresenter @Inject constructor(
return true
}
+ fun onActionModeSelectRestore() {
+ Timber.i("Restore ${messagesToDelete.size} messages")
+ val messageList = messagesToDelete.toList()
+
+ presenterScope.launch {
+ view?.run {
+ showProgress(true)
+ showContent(false)
+ showActionMode(false)
+ }
+ runCatching {
+ val student = studentRepository.getCurrentStudent(true)
+ messageRepository.restoreMessages(student, selectedMailbox, messageList)
+ }
+ .onFailure(errorHandler::dispatch)
+ .onSuccess { view?.showMessage(R.string.message_messages_restored) }
+ }
+ }
+
fun onActionModeSelectDelete() {
- Timber.i("Delete ${messagesToDelete.size} messages)")
+ Timber.i("Delete ${messagesToDelete.size} messages")
val messageList = messagesToDelete.toList()
presenterScope.launch {
@@ -134,7 +153,7 @@ class MessageTabPresenter @Inject constructor(
runCatching {
val student = studentRepository.getCurrentStudent(true)
- messageRepository.deleteMessages(student, selectedMailbox, messageList)
+ messageRepository.deleteMessages(student, messageList)
}
.onFailure(errorHandler::dispatch)
.onSuccess { view?.showMessage(R.string.message_messages_deleted) }
diff --git a/app/src/main/res/drawable/ic_menu_message_delete_forever.xml b/app/src/main/res/drawable/ic_menu_message_delete_forever.xml
new file mode 100644
index 00000000..a7b5ac53
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_message_delete_forever.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_menu_message_restore.xml b/app/src/main/res/drawable/ic_menu_message_restore.xml
new file mode 100644
index 00000000..5c8544f2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_menu_message_restore.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/menu/action_menu_message_preview.xml b/app/src/main/res/menu/action_menu_message_preview.xml
index 22478867..04af8671 100644
--- a/app/src/main/res/menu/action_menu_message_preview.xml
+++ b/app/src/main/res/menu/action_menu_message_preview.xml
@@ -29,6 +29,13 @@
android:title="@string/message_forward"
app:iconTint="@color/material_on_surface_emphasis_medium"
app:showAsAction="ifRoom" />
+
+
-
Select all
Unselect all
+ Restore from trash
Move to trash
Delete permanently
+ Message restored successfully
Message deleted successfully
student
parent
@@ -364,6 +366,7 @@
- %1$d selected
Messages deleted
+ Messages restored
Choose mailbox
Incognito mode is on
Thanks to incognito mode sender is not notified when you read the message
From a7238e3f23703bdcc0af20b06fc09e904fee4ffc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Fri, 1 Mar 2024 22:16:56 +0100
Subject: [PATCH 12/25] New Crowdin updates (#2447)
---
app/src/main/res/values-cs/strings.xml | 3 +++
app/src/main/res/values-de/strings.xml | 3 +++
app/src/main/res/values-pl/strings.xml | 3 +++
app/src/main/res/values-ru/strings.xml | 3 +++
app/src/main/res/values-sk/strings.xml | 3 +++
app/src/main/res/values-uk/strings.xml | 3 +++
6 files changed, 18 insertions(+)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 5c4c52da..fbc92e46 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -336,8 +336,10 @@
Poslat dále
Vybrat vše
Odznačit vše
+ Restore from trash
Přesunout do koše
Odstranit natrvalo
+ Message restored successfully
Zpráva byla úspěšně odstraněna
žák
rodič
@@ -383,6 +385,7 @@
- %1$d vybraných
Zprávy odstraněné
+ Messages restored
Vyberte poštovní schránku
Anonymní režim je zapnutý
Díky anonymnímu režimu není odesílatel upozorněn, když si zprávu přečtete
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index a346bbd2..1f124600 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -296,8 +296,10 @@
Weiterleiten
Alle auswählen
Alle abwählen
+ Restore from trash
In Papierkorb verschieben
Dauerhaft löschen
+ Message restored successfully
Nachricht erfolgreich gelöscht
schüler
Eltern
@@ -335,6 +337,7 @@
- %1$d ausgewählt
Nachrichten gelöscht
+ Messages restored
Postfach auswählen
Incognito mode is on
Thanks to incognito mode sender is not notified when you read the message
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 56a85ea2..597d843d 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -336,8 +336,10 @@
Prześlij dalej
Zaznacz wszystkie
Odznacz wszystkie
+ Przywróć z kosza
Przenieś do kosza
Usuń trwale
+ Wiadomość przywrócona pomyślnie
Wiadomość usunięta pomyślnie
uczeń
rodzic
@@ -383,6 +385,7 @@
- %1$d wybranych
Wiadomości zostały usunięte
+ Wiadomości przywrócone
Wybierz skrzynkę
Tryb incognito jest włączony
Dzięki trybowi incognito nadawca nie zobaczy, że przeczytałeś tę wiadomość
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index f7469675..46a19c71 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -336,8 +336,10 @@
Переслать
Выбрать все
Отменить выбор
+ Restore from trash
Перенести в корзину
Удалить навсегда
+ Message restored successfully
Сообщение успешно удалено
ученик
родитель
@@ -383,6 +385,7 @@
- %1$d выбрано
Сообщение удалено
+ Messages restored
Выбрать почтовый ящик
Incognito mode is on
Thanks to incognito mode sender is not notified when you read the message
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 56238c10..b63c07c6 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -336,8 +336,10 @@
Poslať ďalej
Vybrať všetko
Odznačiť všetko
+ Restore from trash
Presunúť do koša
Odstrániť natrvalo
+ Message restored successfully
Správa bola úspešne odstránená
žiak
rodič
@@ -383,6 +385,7 @@
- %1$d vybraných
Správy odstránené
+ Messages restored
Vyberte poštovú schránku
Režim inkognito je zapnutý
Vďaka inkognito režimu nie je odosielateľ upozornený, keď si správu prečítate
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index a8202747..8116c7e4 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -336,8 +336,10 @@
Переслати
Вибрати всі
Відмінити вибір
+ Restore from trash
Перемістити до кошика
Видалити назавжди
+ Message restored successfully
Лист було успішно видалено
учень
родич
@@ -383,6 +385,7 @@
- %1$d вибрано
Листи видалено
+ Messages restored
Вибрати поштову скриньку
Режим анонімності включено
Завдяки режиму анонімності, відправник не буде сповіщений коли ви прочитаєте повідомлення
From ccba31f2e81d2aa36e3ff64912ad57a5c7a92102 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 2 Mar 2024 16:55:54 +0100
Subject: [PATCH 13/25] Add last announcements to school announcements (#2452)
---
.../61.json | 2533 +++++++++++++++++
.../github/wulkanowy/data/db/AppDatabase.kt | 3 +-
.../data/db/entities/SchoolAnnouncement.kt | 4 +-
.../data/mappers/DirectorInformationMapper.kt | 14 +
.../SchoolAnnouncementRepository.kt | 7 +-
.../SchoolAnnouncementAdapter.kt | 5 +
.../res/layout/item_school_announcement.xml | 33 +-
7 files changed, 2585 insertions(+), 14 deletions(-)
create mode 100644 app/schemas/io.github.wulkanowy.data.db.AppDatabase/61.json
diff --git a/app/schemas/io.github.wulkanowy.data.db.AppDatabase/61.json b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/61.json
new file mode 100644
index 00000000..e36dcc8a
--- /dev/null
+++ b/app/schemas/io.github.wulkanowy.data.db.AppDatabase/61.json
@@ -0,0 +1,2533 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 61,
+ "identityHash": "41fbd2ff00aba10b2ef0a079e6037c87",
+ "entities": [
+ {
+ "tableName": "Students",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`scrapper_base_url` TEXT NOT NULL, `scrapper_domain_suffix` TEXT NOT NULL DEFAULT '', `mobile_base_url` TEXT NOT NULL, `login_type` TEXT NOT NULL, `login_mode` TEXT NOT NULL, `certificate_key` TEXT NOT NULL, `private_key` TEXT NOT NULL, `is_parent` INTEGER NOT NULL, `email` TEXT NOT NULL, `password` TEXT NOT NULL, `symbol` TEXT NOT NULL, `student_id` INTEGER NOT NULL, `user_login_id` INTEGER NOT NULL, `user_name` TEXT NOT NULL, `student_name` TEXT NOT NULL, `school_id` TEXT NOT NULL, `school_short` TEXT NOT NULL, `school_name` TEXT NOT NULL, `class_name` TEXT NOT NULL, `class_id` INTEGER NOT NULL, `is_current` INTEGER NOT NULL, `registration_date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `nick` TEXT NOT NULL, `avatar_color` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "scrapperBaseUrl",
+ "columnName": "scrapper_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "scrapperDomainSuffix",
+ "columnName": "scrapper_domain_suffix",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "mobileBaseUrl",
+ "columnName": "mobile_base_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginType",
+ "columnName": "login_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "loginMode",
+ "columnName": "login_mode",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "certificateKey",
+ "columnName": "certificate_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "privateKey",
+ "columnName": "private_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isParent",
+ "columnName": "is_parent",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "student_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolSymbol",
+ "columnName": "school_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "school_short",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolName",
+ "columnName": "school_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "className",
+ "columnName": "class_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isCurrent",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registrationDate",
+ "columnName": "registration_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nick",
+ "columnName": "nick",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "avatarColor",
+ "columnName": "avatar_color",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_Students_email_symbol_student_id_school_id_class_id",
+ "unique": true,
+ "columnNames": [
+ "email",
+ "symbol",
+ "student_id",
+ "school_id",
+ "class_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Students_email_symbol_student_id_school_id_class_id` ON `${TABLE_NAME}` (`email`, `symbol`, `student_id`, `school_id`, `class_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Semesters",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "kindergartenDiaryId",
+ "columnName": "kindergarten_diary_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "diaryName",
+ "columnName": "diary_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolYear",
+ "columnName": "school_year",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterName",
+ "columnName": "semester_name",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unitId",
+ "columnName": "unit_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "current",
+ "columnName": "is_current",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id",
+ "unique": true,
+ "columnNames": [
+ "student_id",
+ "diary_id",
+ "kindergarten_diary_id",
+ "semester_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `${TABLE_NAME}` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Exams",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `group` TEXT NOT NULL, `type` TEXT NOT NULL, `description` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Timetable",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `number` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `subjectOld` TEXT NOT NULL, `group` TEXT NOT NULL, `room` TEXT NOT NULL, `roomOld` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacherOld` TEXT NOT NULL, `info` TEXT NOT NULL, `student_plan` INTEGER NOT NULL, `changes` INTEGER NOT NULL, `canceled` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectOld",
+ "columnName": "subjectOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "group",
+ "columnName": "group",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "room",
+ "columnName": "room",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "roomOld",
+ "columnName": "roomOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherOld",
+ "columnName": "teacherOld",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "info",
+ "columnName": "info",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isStudentPlan",
+ "columnName": "student_plan",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "changes",
+ "columnName": "changes",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "canceled",
+ "columnName": "canceled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Attendance",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `time_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `excused` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `excusable` INTEGER NOT NULL, `excuse_status` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "timeId",
+ "columnName": "time_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presence",
+ "columnName": "presence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "exemption",
+ "columnName": "exemption",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lateness",
+ "columnName": "lateness",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excused",
+ "columnName": "excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deleted",
+ "columnName": "deleted",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excusable",
+ "columnName": "excusable",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "excuseStatus",
+ "columnName": "excuse_status",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AttendanceSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `subject_id` INTEGER NOT NULL, `month` INTEGER NOT NULL, `presence` INTEGER NOT NULL, `absence` INTEGER NOT NULL, `absence_excused` INTEGER NOT NULL, `absence_for_school_reasons` INTEGER NOT NULL, `lateness` INTEGER NOT NULL, `lateness_excused` INTEGER NOT NULL, `exemption` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subjectId",
+ "columnName": "subject_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "month",
+ "columnName": "month",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presence",
+ "columnName": "presence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absenceExcused",
+ "columnName": "absence_excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absenceForSchoolReasons",
+ "columnName": "absence_for_school_reasons",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lateness",
+ "columnName": "lateness",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "latenessExcused",
+ "columnName": "lateness_excused",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "exemption",
+ "columnName": "exemption",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Grades",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `entry` TEXT NOT NULL, `value` REAL NOT NULL, `modifier` REAL NOT NULL, `comment` TEXT NOT NULL, `color` TEXT NOT NULL, `grade_symbol` TEXT NOT NULL, `description` TEXT NOT NULL, `weight` TEXT NOT NULL, `weightValue` REAL NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entry",
+ "columnName": "entry",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "value",
+ "columnName": "value",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "modifier",
+ "columnName": "modifier",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "comment",
+ "columnName": "comment",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "color",
+ "columnName": "color",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gradeSymbol",
+ "columnName": "grade_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "weight",
+ "columnName": "weight",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "weightValue",
+ "columnName": "weightValue",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isRead",
+ "columnName": "is_read",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesSummary",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `position` INTEGER NOT NULL, `subject` TEXT NOT NULL, `predicted_grade` TEXT NOT NULL, `final_grade` TEXT NOT NULL, `proposed_points` TEXT NOT NULL, `final_points` TEXT NOT NULL, `points_sum` TEXT NOT NULL, `average` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_predicted_grade_notified` INTEGER NOT NULL, `is_final_grade_notified` INTEGER NOT NULL, `predicted_grade_last_change` INTEGER NOT NULL, `final_grade_last_change` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "position",
+ "columnName": "position",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGrade",
+ "columnName": "predicted_grade",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGrade",
+ "columnName": "final_grade",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "proposedPoints",
+ "columnName": "proposed_points",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalPoints",
+ "columnName": "final_points",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "pointsSum",
+ "columnName": "points_sum",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "average",
+ "columnName": "average",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPredictedGradeNotified",
+ "columnName": "is_predicted_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isFinalGradeNotified",
+ "columnName": "is_final_grade_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "predictedGradeLastChange",
+ "columnName": "predicted_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "finalGradeLastChange",
+ "columnName": "final_grade_last_change",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradePartialStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `class_average` TEXT NOT NULL, `student_average` TEXT NOT NULL, `class_amounts` TEXT NOT NULL, `student_amounts` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAverage",
+ "columnName": "class_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAverage",
+ "columnName": "student_average",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classAmounts",
+ "columnName": "class_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentAmounts",
+ "columnName": "student_amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesPointsStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `others` REAL NOT NULL, `student` REAL NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "others",
+ "columnName": "others",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "student",
+ "columnName": "student",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradeSemesterStatistics",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `amounts` TEXT NOT NULL, `student_grade` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "amounts",
+ "columnName": "amounts",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentGrade",
+ "columnName": "student_grade",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Messages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`email` TEXT NOT NULL, `message_global_key` TEXT NOT NULL, `mailbox_key` TEXT NOT NULL, `message_id` INTEGER NOT NULL, `correspondents` TEXT NOT NULL, `subject` TEXT NOT NULL, `date` INTEGER NOT NULL, `folder_id` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `read_by` INTEGER, `unread_by` INTEGER, `has_attachments` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL, `content` TEXT NOT NULL, `sender` TEXT, `recipients` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mailboxKey",
+ "columnName": "mailbox_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "messageId",
+ "columnName": "message_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondents",
+ "columnName": "correspondents",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "folderId",
+ "columnName": "folder_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "unread",
+ "columnName": "unread",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "readBy",
+ "columnName": "read_by",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "unreadBy",
+ "columnName": "unread_by",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "hasAttachments",
+ "columnName": "has_attachments",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sender",
+ "columnName": "sender",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "recipients",
+ "columnName": "recipients",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MessageAttachments",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`message_global_key` TEXT NOT NULL, `url` TEXT NOT NULL, `filename` TEXT NOT NULL, PRIMARY KEY(`message_global_key`, `url`, `filename`))",
+ "fields": [
+ {
+ "fieldPath": "messageGlobalKey",
+ "columnName": "message_global_key",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "filename",
+ "columnName": "filename",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "message_global_key",
+ "url",
+ "filename"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `category` TEXT NOT NULL, `category_type` INTEGER NOT NULL, `is_points_show` INTEGER NOT NULL, `points` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_read` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "category",
+ "columnName": "category",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "categoryType",
+ "columnName": "category_type",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isPointsShow",
+ "columnName": "is_points_show",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "points",
+ "columnName": "points",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isRead",
+ "columnName": "is_read",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Homework",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `entry_date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `attachments` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_done` INTEGER NOT NULL, `is_notified` INTEGER NOT NULL, `is_added_by_user` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entryDate",
+ "columnName": "entry_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attachments",
+ "columnName": "attachments",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isDone",
+ "columnName": "is_done",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Subjects",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `real_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "realId",
+ "columnName": "real_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "LuckyNumbers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `lucky_number` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "luckyNumber",
+ "columnName": "lucky_number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "CompletedLesson",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `number` INTEGER NOT NULL, `subject` TEXT NOT NULL, `topic` TEXT NOT NULL, `teacher` TEXT NOT NULL, `teacher_symbol` TEXT NOT NULL, `substitution` TEXT NOT NULL, `absence` TEXT NOT NULL, `resources` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "number",
+ "columnName": "number",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "topic",
+ "columnName": "topic",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacher",
+ "columnName": "teacher",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "teacherSymbol",
+ "columnName": "teacher_symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "substitution",
+ "columnName": "substitution",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "absence",
+ "columnName": "absence",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "resources",
+ "columnName": "resources",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Mailboxes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`globalKey` TEXT NOT NULL, `email` TEXT NOT NULL, `symbol` TEXT NOT NULL, `schoolId` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `studentName` TEXT NOT NULL, `schoolNameShort` TEXT NOT NULL, `type` TEXT NOT NULL, PRIMARY KEY(`globalKey`))",
+ "fields": [
+ {
+ "fieldPath": "globalKey",
+ "columnName": "globalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "symbol",
+ "columnName": "symbol",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolId",
+ "columnName": "schoolId",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentName",
+ "columnName": "studentName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolNameShort",
+ "columnName": "schoolNameShort",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "globalKey"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Recipients",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mailboxGlobalKey` TEXT NOT NULL, `studentMailboxGlobalKey` TEXT NOT NULL, `fullName` TEXT NOT NULL, `userName` TEXT NOT NULL, `schoolShortName` TEXT NOT NULL, `type` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "mailboxGlobalKey",
+ "columnName": "mailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentMailboxGlobalKey",
+ "columnName": "studentMailboxGlobalKey",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "fullName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "userName",
+ "columnName": "userName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "schoolShortName",
+ "columnName": "schoolShortName",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MobileDevices",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `device_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceId",
+ "columnName": "device_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Teachers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "shortName",
+ "columnName": "short_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "School",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "classId",
+ "columnName": "class_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "contact",
+ "columnName": "contact",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "headmaster",
+ "columnName": "headmaster",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "pedagogue",
+ "columnName": "pedagogue",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Conferences",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `subject` TEXT NOT NULL, `agenda` TEXT NOT NULL, `present_on_conference` TEXT NOT NULL, `conference_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "agenda",
+ "columnName": "agenda",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "presentOnConference",
+ "columnName": "present_on_conference",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "conferenceId",
+ "columnName": "conference_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableAdditional",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `repeat_id` BLOB DEFAULT NULL, `is_added_by_user` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "start",
+ "columnName": "start",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "end",
+ "columnName": "end",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatId",
+ "columnName": "repeat_id",
+ "affinity": "BLOB",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "isAddedByUser",
+ "columnName": "is_added_by_user",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "StudentInfo",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `full_name` TEXT NOT NULL, `first_name` TEXT NOT NULL, `second_name` TEXT NOT NULL, `surname` TEXT NOT NULL, `birth_date` INTEGER NOT NULL, `birth_place` TEXT NOT NULL, `gender` TEXT NOT NULL, `has_polish_citizenship` INTEGER NOT NULL, `family_name` TEXT NOT NULL, `parents_names` TEXT NOT NULL, `address` TEXT NOT NULL, `registered_address` TEXT NOT NULL, `correspondence_address` TEXT NOT NULL, `phone_number` TEXT NOT NULL, `cell_phone_number` TEXT NOT NULL, `email` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_guardian_full_name` TEXT, `first_guardian_kinship` TEXT, `first_guardian_address` TEXT, `first_guardian_phones` TEXT, `first_guardian_email` TEXT, `second_guardian_full_name` TEXT, `second_guardian_kinship` TEXT, `second_guardian_address` TEXT, `second_guardian_phones` TEXT, `second_guardian_email` TEXT)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "fullName",
+ "columnName": "full_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstName",
+ "columnName": "first_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "secondName",
+ "columnName": "second_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "surname",
+ "columnName": "surname",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthDate",
+ "columnName": "birth_date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "birthPlace",
+ "columnName": "birth_place",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "gender",
+ "columnName": "gender",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hasPolishCitizenship",
+ "columnName": "has_polish_citizenship",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "familyName",
+ "columnName": "family_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "parentsNames",
+ "columnName": "parents_names",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "address",
+ "columnName": "address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registeredAddress",
+ "columnName": "registered_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "correspondenceAddress",
+ "columnName": "correspondence_address",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "phoneNumber",
+ "columnName": "phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "cellPhoneNumber",
+ "columnName": "cell_phone_number",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "email",
+ "columnName": "email",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "firstGuardian.fullName",
+ "columnName": "first_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.kinship",
+ "columnName": "first_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.address",
+ "columnName": "first_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.phones",
+ "columnName": "first_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "firstGuardian.email",
+ "columnName": "first_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.fullName",
+ "columnName": "second_guardian_full_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.kinship",
+ "columnName": "second_guardian_kinship",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.address",
+ "columnName": "second_guardian_address",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.phones",
+ "columnName": "second_guardian_phones",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "secondGuardian.email",
+ "columnName": "second_guardian_email",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "TimetableHeaders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `content` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "diaryId",
+ "columnName": "diary_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SchoolAnnouncements",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`user_login_id` INTEGER NOT NULL, `date` INTEGER NOT NULL, `subject` TEXT NOT NULL, `content` TEXT NOT NULL, `author` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "userLoginId",
+ "columnName": "user_login_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "author",
+ "columnName": "author",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Notifications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`student_id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `type` TEXT NOT NULL, `destination` TEXT NOT NULL DEFAULT '{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}', `date` INTEGER NOT NULL, `data` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "destination",
+ "columnName": "destination",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}'"
+ },
+ {
+ "fieldPath": "date",
+ "columnName": "date",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "AdminMessages",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `version_name` INTEGER, `version_max` INTEGER, `target_register_host` TEXT, `target_flavor` TEXT, `destination_url` TEXT, `priority` TEXT NOT NULL, `types` TEXT NOT NULL DEFAULT '[]', `is_ok_visible` INTEGER NOT NULL DEFAULT 0, `is_x_visible` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "versionMin",
+ "columnName": "version_name",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "versionMax",
+ "columnName": "version_max",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetRegisterHost",
+ "columnName": "target_register_host",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "targetFlavor",
+ "columnName": "target_flavor",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "destinationUrl",
+ "columnName": "destination_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "priority",
+ "columnName": "priority",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "types",
+ "columnName": "types",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'[]'"
+ },
+ {
+ "fieldPath": "isOkVisible",
+ "columnName": "is_ok_visible",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "isXVisible",
+ "columnName": "is_x_visible",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "MutedMessageSenders",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`author` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "author",
+ "columnName": "author",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "GradesDescriptive",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`semester_id` INTEGER NOT NULL, `student_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `description` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_notified` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "semesterId",
+ "columnName": "semester_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "studentId",
+ "columnName": "student_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subject",
+ "columnName": "subject",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "description",
+ "columnName": "description",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isNotified",
+ "columnName": "is_notified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "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, '41fbd2ff00aba10b2ef0a079e6037c87')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt b/app/src/main/java/io/github/wulkanowy/data/db/AppDatabase.kt
index 21a6e3f3..208daf75 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
@@ -173,6 +173,7 @@ import javax.inject.Singleton
AutoMigration(from = 57, to = 58, spec = Migration58::class),
AutoMigration(from = 58, to = 59),
AutoMigration(from = 59, to = 60),
+ AutoMigration(from = 60, to = 61),
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@@ -181,7 +182,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
- const val VERSION_SCHEMA = 60
+ const val VERSION_SCHEMA = 61
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt b/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt
index 25e27ef1..ac096b02 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/entities/SchoolAnnouncement.kt
@@ -16,7 +16,9 @@ data class SchoolAnnouncement(
val subject: String,
- val content: String
+ val content: String,
+
+ val author: String? = null,
) : Serializable {
@PrimaryKey(autoGenerate = true)
diff --git a/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt
index 16f1bbac..85b37afc 100644
--- a/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/mappers/DirectorInformationMapper.kt
@@ -3,12 +3,26 @@ package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformation
+import io.github.wulkanowy.sdk.pojo.LastAnnouncement as SdkLastAnnouncement
+@JvmName("mapDirectorInformationToEntities")
fun List.mapToEntities(student: Student) = map {
SchoolAnnouncement(
userLoginId = student.userLoginId,
date = it.date,
subject = it.subject,
content = it.content,
+ author = null,
+ )
+}
+
+@JvmName("mapLastAnnouncementsToEntities")
+fun List.mapToEntities(student: Student) = map {
+ SchoolAnnouncement(
+ userLoginId = student.userLoginId,
+ date = it.date,
+ subject = it.subject,
+ content = it.content,
+ author = it.author,
)
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
index 4c42d092..8537fbc3 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
@@ -41,9 +41,10 @@ class SchoolAnnouncementRepository @Inject constructor(
schoolAnnouncementDb.loadAll(student.userLoginId)
},
fetch = {
- sdk.init(student)
- .getDirectorInformation()
- .mapToEntities(student)
+ val sdk = sdk.init(student)
+ val lastAnnouncements = sdk.getLastAnnouncements().mapToEntities(student)
+ val directorInformation = sdk.getDirectorInformation().mapToEntities(student)
+ lastAnnouncements + directorInformation
},
saveFetchResult = { old, new ->
val schoolAnnouncementsToSave = (new uniqueSubtract old).onEach {
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt
index 46999599..731488a9 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/schoolannouncement/SchoolAnnouncementAdapter.kt
@@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.schoolannouncement
import android.view.LayoutInflater
import android.view.ViewGroup
+import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
import io.github.wulkanowy.databinding.ItemSchoolAnnouncementBinding
@@ -29,6 +30,10 @@ class SchoolAnnouncementAdapter @Inject constructor() :
schoolAnnouncementItemDate.text = item.date.toFormattedString()
schoolAnnouncementItemType.text = item.subject
schoolAnnouncementItemContent.text = item.content.parseUonetHtml()
+ with(schoolAnnouncementItemAuthor) {
+ text = item.author
+ isVisible = !item.author.isNullOrBlank()
+ }
root.setOnClickListener { onItemClickListener(item) }
}
diff --git a/app/src/main/res/layout/item_school_announcement.xml b/app/src/main/res/layout/item_school_announcement.xml
index bb0cffd1..1197c66c 100644
--- a/app/src/main/res/layout/item_school_announcement.xml
+++ b/app/src/main/res/layout/item_school_announcement.xml
@@ -11,27 +11,41 @@
android:id="@+id/schoolAnnouncementItemDate"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginStart="15dp"
+ android:layout_marginHorizontal="15dp"
android:layout_marginTop="10dp"
- android:layout_marginEnd="10dp"
android:textColor="?android:textColorSecondary"
android:textSize="15sp"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/schoolAnnouncementItemAuthor"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/date/ddmmyy" />
+
+
@@ -40,6 +54,7 @@
android:id="@+id/schoolAnnouncementItemContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_marginHorizontal="15dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="15dp"
android:ellipsize="end"
@@ -47,8 +62,8 @@
android:maxLines="2"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="@+id/schoolAnnouncementItemType"
- app:layout_constraintStart_toStartOf="@id/schoolAnnouncementItemDate"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/schoolAnnouncementItemType"
tools:text="@tools:sample/lorem/random" />
From f2d26453ed330e49590194ceda5bc3c5f3b8e822 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 2 Mar 2024 17:01:12 +0100
Subject: [PATCH 14/25] Fix calculating average with optional arithmetic
average on and no grade with average in second semester (#2448)
---
.../ui/modules/grade/GradeAverageProvider.kt | 27 ++++++---
.../modules/grade/GradeAverageProviderTest.kt | 56 +++++++++++++++++--
2 files changed, 71 insertions(+), 12 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt
index e8a5fa25..8da59eaf 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProvider.kt
@@ -159,7 +159,7 @@ class GradeAverageProvider @Inject constructor(
?.updateModifiers(student, config).orEmpty()
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(
- config.isOptionalArithmeticAverage
+ isOptionalArithmeticAverage = config.isOptionalArithmeticAverage,
)
} else {
secondSemesterSubject.average
@@ -173,13 +173,21 @@ class GradeAverageProvider @Inject constructor(
config: AverageCalcParams,
): Double {
return if (!isAnyVulcanAverage || config.forceAverageCalc) {
- val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
+ val isSecondSemesterHasWeightGrade = secondSemesterSubject.grades
+ .any { it.weightValue > .0 }
+ val isSecondSemesterHasArithmeticGrade = secondSemesterSubject.grades
+ .all { it.weightValue == .0 } && config.isOptionalArithmeticAverage
+ val isSecondSemesterHaveAverage =
+ isSecondSemesterHasWeightGrade || isSecondSemesterHasArithmeticGrade
+
+ val divider = if (isSecondSemesterHaveAverage) 2 else 1
val secondSemesterAverage = secondSemesterSubject.grades
.updateModifiers(student, config)
- .calcAverage(config.isOptionalArithmeticAverage)
+ .calcAverage(isOptionalArithmeticAverage = config.isOptionalArithmeticAverage)
val firstSemesterAverage = firstSemesterSubject?.grades
?.updateModifiers(student, config)
- ?.calcAverage(config.isOptionalArithmeticAverage) ?: secondSemesterAverage
+ ?.calcAverage(isOptionalArithmeticAverage = config.isOptionalArithmeticAverage)
+ ?: secondSemesterAverage
(secondSemesterAverage + firstSemesterAverage) / divider
} else {
@@ -225,7 +233,7 @@ class GradeAverageProvider @Inject constructor(
subject = summary.subject,
average = if (!isAnyAverage || params.forceAverageCalc) {
grades.updateModifiers(student, params)
- .calcAverage(params.isOptionalArithmeticAverage)
+ .calcAverage(isOptionalArithmeticAverage = params.isOptionalArithmeticAverage)
} else summary.average,
points = summary.pointsSum,
summary = summary,
@@ -286,8 +294,13 @@ class GradeAverageProvider @Inject constructor(
proposedPoints = "",
finalPoints = "",
pointsSum = "",
- average = if (calcAverage) details.updateModifiers(student, params)
- .calcAverage(params.isOptionalArithmeticAverage) else .0
+ average = when {
+ calcAverage -> details
+ .updateModifiers(student, params)
+ .calcAverage(isOptionalArithmeticAverage = params.isOptionalArithmeticAverage)
+
+ else -> .0
+ }
)
}
}
diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
index 6a717f6f..4f0f80fe 100644
--- a/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/ui/modules/grade/GradeAverageProviderTest.kt
@@ -25,6 +25,7 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -112,8 +113,8 @@ class GradeAverageProviderTest {
private val secondGradeWithModifier = listOf(
// avg: 3.375
- getGrade(24, "Język polski", 3.0, -0.50),
- getGrade(24, "Język polski", 4.0, 0.25)
+ getGrade(24, "Język polski", 3.0, -0.50, entry = "3-"),
+ getGrade(24, "Język polski", 4.0, 0.25, entry = "4+")
)
private val secondSummariesWithModifier = listOf(
@@ -122,8 +123,8 @@ class GradeAverageProviderTest {
private val noWeightGrades = listOf(
// standard: 0.0, arithmetic: 4.0
- getGrade(22, "Matematyka", 5.0, 0.0, 0.0),
- getGrade(22, "Matematyka", 3.0, 0.0, 0.0),
+ getGrade(22, "Matematyka", 5.0, 0.0, 0.0, "5"),
+ getGrade(22, "Matematyka", 3.0, 0.0, 0.0, "3"),
getGrade(22, "Matematyka", 1.0, 0.0, 0.0, "np.")
)
@@ -132,7 +133,7 @@ class GradeAverageProviderTest {
)
private val noWeightGradesArithmeticSummary = listOf(
- getSummary(23, "Matematyka", 4.0)
+ getSummary(23, "Matematyka", .0)
)
@Before
@@ -211,6 +212,51 @@ class GradeAverageProviderTest {
) // from summary: 4,0
}
+ @Test
+ fun `calc current semester arithmetic average with no weights in second semester`() = runTest {
+ every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(false)
+ every { preferencesRepository.isOptionalArithmeticAverageFlow } returns flowOf(true)
+ every { preferencesRepository.gradeAverageModeFlow } returns flowOf(GradeAverageMode.BOTH_SEMESTERS)
+ coEvery {
+ gradeRepository.getGrades(
+ student = student,
+ semester = semesters[1],
+ forceRefresh = true,
+ )
+ } returns resourceFlow {
+ Triple(
+ first = noWeightGrades,
+ second = noWeightGradesArithmeticSummary,
+ third = emptyList(),
+ )
+ }
+ coEvery {
+ gradeRepository.getGrades(
+ student = student,
+ semester = semesters[2],
+ forceRefresh = true,
+ )
+ } returns resourceFlow {
+ Triple(
+ first = noWeightGrades,
+ second = noWeightGradesArithmeticSummary,
+ third = emptyList(),
+ )
+ }
+
+ val items = gradeAverageProvider.getGradesDetailsWithAverage(
+ student = student,
+ semesterId = semesters[2].semesterId,
+ forceRefresh = true
+ ).getResult()
+
+ assertEquals(
+ 4.0,
+ items.single { it.subject == "Matematyka" }.average,
+ .0
+ ) // from summary: 4,0
+ }
+
@Test
fun `calc current semester average with load from cache sequence`() {
every { preferencesRepository.gradeAverageForceCalcFlow } returns flowOf(true)
From 3564366a8f04aafc5ba1a91b875ba39f62eda798 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Mar 2024 16:08:32 +0000
Subject: [PATCH 15/25] Bump com.google.firebase:firebase-bom from 32.7.2 to
32.7.3 (#2453)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index 07efeb2f..f4ead943 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -252,7 +252,7 @@ dependencies {
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
implementation 'org.apache.commons:commons-text:1.11.0'
- playImplementation platform('com.google.firebase:firebase-bom:32.7.2')
+ playImplementation platform('com.google.firebase:firebase-bom:32.7.3')
playImplementation 'com.google.firebase:firebase-analytics'
playImplementation 'com.google.firebase:firebase-messaging'
playImplementation 'com.google.firebase:firebase-crashlytics:'
From 05bda598fc5e60ff1c9ea88efda13f27a5007fbe Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Mar 2024 16:10:19 +0000
Subject: [PATCH 16/25] Bump mockk from 1.13.9 to 1.13.10 (#2455)
---
app/build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle b/app/build.gradle
index f4ead943..f01b9917 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -190,7 +190,7 @@ ext {
android_hilt = "1.2.0"
room = "2.6.1"
chucker = "4.0.0"
- mockk = "1.13.9"
+ mockk = "1.13.10"
coroutines = "1.8.0"
}
From e9d64de0cbe88fec1a228a00a934d0b9b300dc81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 2 Mar 2024 17:10:38 +0100
Subject: [PATCH 17/25] Improve invalid password message (#2451)
---
.../github/wulkanowy/ui/base/BaseActivity.kt | 25 +++++++++++++++++++
.../wulkanowy/utils/ExceptionExtension.kt | 2 ++
app/src/main/res/values/strings.xml | 5 ++--
3 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt
index 29996db7..10735dab 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt
@@ -17,6 +17,8 @@ import io.github.wulkanowy.utils.FragmentLifecycleLogger
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.lifecycleAwareVariable
import io.github.wulkanowy.utils.openInternetBrowser
+import timber.log.Timber
+import java.time.Instant
import javax.inject.Inject
abstract class BaseActivity, VB : ViewBinding> :
@@ -36,6 +38,8 @@ abstract class BaseActivity, VB : ViewBinding> :
abstract var presenter: T
+ private var lastDialogOpenTime = mutableMapOf()
+
override fun onCreate(savedInstanceState: Bundle?) {
inject()
themeManager.applyActivityTheme(this)
@@ -70,6 +74,8 @@ abstract class BaseActivity, VB : ViewBinding> :
}
override fun showExpiredCredentialsDialog() {
+ if (!shouldShowDialog(DIALOG_ERROR_BAD_CREDENTIALS)) return
+
MaterialAlertDialogBuilder(this)
.setTitle(R.string.main_expired_credentials_title)
.setMessage(R.string.main_expired_credentials_description)
@@ -83,6 +89,8 @@ abstract class BaseActivity, VB : ViewBinding> :
}
override fun showDecryptionFailedDialog() {
+ if (!shouldShowDialog(DIALOG_ERROR_DECRYPTION_FAILED)) return
+
MaterialAlertDialogBuilder(this)
.setTitle(R.string.main_session_expired)
.setMessage(R.string.main_session_relogin)
@@ -119,4 +127,21 @@ abstract class BaseActivity, VB : ViewBinding> :
protected open fun inject() {
throw UnsupportedOperationException()
}
+
+ private fun shouldShowDialog(name: String): Boolean {
+ val lastOpenTime = lastDialogOpenTime[name]
+ val now = Instant.now()
+
+ if (lastOpenTime != null && now.isBefore(lastOpenTime.plusSeconds(1))) {
+ Timber.i("Dialog $name was shown less than a second ago. Skip")
+ return false
+ }
+ lastDialogOpenTime[name] = Instant.now()
+ return true
+ }
+
+ companion object {
+ private const val DIALOG_ERROR_BAD_CREDENTIALS = "dialog_error_bad_credentials"
+ private const val DIALOG_ERROR_DECRYPTION_FAILED = "dialog_error_decryption_failed"
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
index 1c229051..d541c0a7 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/ExceptionExtension.kt
@@ -9,6 +9,7 @@ import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException
import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
import io.github.wulkanowy.sdk.scrapper.exception.VulcanException
+import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
import okhttp3.internal.http2.StreamResetException
@@ -34,6 +35,7 @@ fun Resources.getErrorString(error: Throwable): String = when (error) {
is ServiceUnavailableException -> R.string.error_service_unavailable
is FeatureDisabledException -> R.string.error_feature_disabled
is FeatureNotAvailableException -> R.string.error_feature_not_available
+ is BadCredentialsException -> R.string.error_password_invalid
is AccountInactiveException -> R.string.error_account_inactive
is VulcanException -> R.string.error_unknown_uonet
is ScrapperException -> R.string.error_unknown_app
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6843f1aa..266c3522 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -109,8 +109,8 @@
Log in
Session expired
Session expired, log in again
- Your account password has been changed. You need to log in to Wulkanowy again
- Password changed
+ Password has expired or been changed
+ Your account password has expired or been changed. You will need to log in to Wulkanowy again
Application support
Do you like this app? Support its development by enabling non-invasive ads that you can disable at any time
Enable ads
@@ -858,6 +858,7 @@
This account is inactive. Try logging in again
Connection to register failed. Servers can be overloaded. Please try again later
Loading data failed. Please try again later
+ Your password has expired or been changed. Please log in again
Register password change required
Maintenance underway UONET + register. Try again later
Unknown UONET + register error. Try again later
From dc9af29a441991610e79e25bd8de529d14b3f019 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Mar 2024 16:11:13 +0000
Subject: [PATCH 18/25] Bump hilt_version from 2.50 to 2.51 (#2456)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index f7f3d209..a23f2191 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ buildscript {
ext {
kotlin_version = '1.9.22'
about_libraries = '10.10.0'
- hilt_version = '2.50'
+ hilt_version = '2.51'
}
repositories {
mavenCentral()
From fb240938ed69801da29040a16245079c5989ac1d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 2 Mar 2024 16:21:55 +0000
Subject: [PATCH 19/25] Bump about_libraries from 10.10.0 to 11.1.0 (#2454)
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index a23f2191..ec19ee49 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
buildscript {
ext {
kotlin_version = '1.9.22'
- about_libraries = '10.10.0'
+ about_libraries = '11.1.0'
hilt_version = '2.51'
}
repositories {
From 333306e7ba57c278bafe33e0310cc7e88eb5574b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 2 Mar 2024 17:25:27 +0100
Subject: [PATCH 20/25] Wrap delete and save operations in database
transactions (#2450)
---
.../wulkanowy/data/db/dao/AdminMessageDao.kt | 14 +--
.../github/wulkanowy/data/db/dao/BaseDao.kt | 7 ++
.../data/repositories/AttendanceRepository.kt | 7 +-
.../AttendanceSummaryRepository.kt | 9 +-
.../CompletedLessonsRepository.kt | 14 ++-
.../data/repositories/ConferenceRepository.kt | 12 +-
.../data/repositories/ExamRepository.kt | 12 +-
.../data/repositories/GradeRepository.kt | 87 +++++++------
.../repositories/GradeStatisticsRepository.kt | 22 ++--
.../data/repositories/HomeworkRepository.kt | 12 +-
.../repositories/LuckyNumberRepository.kt | 11 +-
.../data/repositories/MessageRepository.kt | 13 +-
.../repositories/MobileDeviceRepository.kt | 7 +-
.../data/repositories/NoteRepository.kt | 17 ++-
.../data/repositories/RecipientRepository.kt | 12 +-
.../SchoolAnnouncementRepository.kt | 12 +-
.../data/repositories/SchoolRepository.kt | 8 +-
.../data/repositories/SemesterRepository.kt | 15 ++-
.../repositories/StudentInfoRepository.kt | 10 +-
.../data/repositories/SubjectRepository.kt | 7 +-
.../data/repositories/TeacherRepository.kt | 7 +-
.../data/repositories/TimetableRepository.kt | 18 ++-
.../repositories/AttendanceRepositoryTest.kt | 87 +++++++++----
.../CompletedLessonsRepositoryTest.kt | 117 +++++++++++-------
.../data/repositories/ExamRemoteTest.kt | 66 ++++++----
.../data/repositories/GradeRepositoryTest.kt | 81 +++++++-----
.../GradeStatisticsRepositoryTest.kt | 12 +-
.../repositories/LuckyNumberRemoteTest.kt | 51 +++++---
.../repositories/MessageRepositoryTest.kt | 17 +--
.../MobileDeviceRepositoryTest.kt | 101 +++++++++------
.../data/repositories/RecipientLocalTest.kt | 20 ++-
.../repositories/SemesterRepositoryTest.kt | 51 +++++---
.../repositories/TimetableRepositoryTest.kt | 17 +--
33 files changed, 587 insertions(+), 366 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/AdminMessageDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/AdminMessageDao.kt
index 2b4cb597..6c8d7e47 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/AdminMessageDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/AdminMessageDao.kt
@@ -2,24 +2,14 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
-import androidx.room.Transaction
import io.github.wulkanowy.data.db.entities.AdminMessage
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@Dao
-abstract class AdminMessageDao : BaseDao {
+interface AdminMessageDao : BaseDao {
@Query("SELECT * FROM AdminMessages")
- abstract fun loadAll(): Flow>
-
- @Transaction
- open suspend fun removeOldAndSaveNew(
- oldMessages: List,
- newMessages: List
- ) {
- deleteAll(oldMessages)
- insertAll(newMessages)
- }
+ fun loadAll(): Flow>
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt
index 056a5cbd..937e9824 100644
--- a/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/BaseDao.kt
@@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
+import androidx.room.Transaction
import androidx.room.Update
interface BaseDao {
@@ -15,4 +16,10 @@ interface BaseDao {
@Delete
suspend fun deleteAll(items: List)
+
+ @Transaction
+ suspend fun removeOldAndSaveNew(oldItems: List, newItems: List) {
+ deleteAll(oldItems)
+ insertAll(newItems)
+ }
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
index bbf627de..46ea29f8 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt
@@ -65,12 +65,13 @@ class AttendanceRepository @Inject constructor(
.mapToEntities(semester, lessons)
},
saveFetchResult = { old, new ->
- attendanceDb.deleteAll(old uniqueSubtract new)
val attendanceToAdd = (new uniqueSubtract old).map { newAttendance ->
newAttendance.apply { if (notify) isNotified = false }
}
- attendanceDb.insertAll(attendanceToAdd)
-
+ attendanceDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = attendanceToAdd,
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
},
filterResult = { it.filter { item -> item.date in start..end } }
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
index 6bdcf9d7..c6cfc2f6 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt
@@ -1,5 +1,7 @@
package io.github.wulkanowy.data.repositories
+import androidx.room.withTransaction
+import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
@@ -20,6 +22,7 @@ class AttendanceSummaryRepository @Inject constructor(
private val attendanceDb: AttendanceSummaryDao,
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
+ private val appDatabase: AppDatabase,
) {
private val saveFetchResultMutex = Mutex()
@@ -46,8 +49,10 @@ class AttendanceSummaryRepository @Inject constructor(
.mapToEntities(semester, subjectId)
},
saveFetchResult = { old, new ->
- attendanceDb.deleteAll(old uniqueSubtract new)
- attendanceDb.insertAll(new uniqueSubtract old)
+ appDatabase.withTransaction {
+ attendanceDb.deleteAll(old uniqueSubtract new)
+ attendanceDb.insertAll(new uniqueSubtract old)
+ }
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
index 1579ae62..f7f86b23 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt
@@ -6,7 +6,13 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.*
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.monday
+import io.github.wulkanowy.utils.sunday
+import io.github.wulkanowy.utils.switchSemester
+import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.sync.Mutex
import java.time.LocalDate
import javax.inject.Inject
@@ -53,8 +59,10 @@ class CompletedLessonsRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
- completedLessonsDb.deleteAll(old uniqueSubtract new)
- completedLessonsDb.insertAll(new uniqueSubtract old)
+ completedLessonsDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = new uniqueSubtract old,
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
},
filterResult = { it.filter { item -> item.date in start..end } }
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
index 7eb37f0b..fbe57860 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt
@@ -53,12 +53,12 @@ class ConferenceRepository @Inject constructor(
.filter { it.date >= startDate }
},
saveFetchResult = { old, new ->
- val conferencesToSave = (new uniqueSubtract old).onEach {
- if (notify) it.isNotified = false
- }
-
- conferenceDb.deleteAll(old uniqueSubtract new)
- conferenceDb.insertAll(conferencesToSave)
+ conferenceDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = (new uniqueSubtract old).onEach {
+ if (notify) it.isNotified = false
+ },
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
index 96026a55..9b8dd02e 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt
@@ -62,12 +62,12 @@ class ExamRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
- val examsToSave = (new uniqueSubtract old).onEach {
- if (notify) it.isNotified = false
- }
-
- examDb.deleteAll(old uniqueSubtract new)
- examDb.insertAll(examsToSave)
+ examDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = (new uniqueSubtract old).onEach {
+ if (notify) it.isNotified = false
+ },
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
},
filterResult = { it.filter { item -> item.date in start..end } }
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
index 1e2ea935..ac1ef541 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt
@@ -87,10 +87,12 @@ class GradeRepository @Inject constructor(
new: List,
notify: Boolean
) {
- gradeDescriptiveDb.deleteAll(old uniqueSubtract new)
- gradeDescriptiveDb.insertAll((new uniqueSubtract old).onEach {
- if (notify) it.isNotified = false
- })
+ gradeDescriptiveDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = (new uniqueSubtract old).onEach {
+ if (notify) it.isNotified = false
+ },
+ )
}
private suspend fun refreshGradeDetails(
@@ -101,13 +103,16 @@ class GradeRepository @Inject constructor(
) {
val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date
?: student.registrationDate.toLocalDate()
- gradeDb.deleteAll(oldGrades uniqueSubtract newDetails)
- gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach {
- if (it.date >= notifyBreakDate) it.apply {
- isRead = false
- if (notify) isNotified = false
- }
- })
+
+ gradeDb.removeOldAndSaveNew(
+ oldItems = oldGrades uniqueSubtract newDetails,
+ newItems = (newDetails uniqueSubtract oldGrades).onEach {
+ if (it.date >= notifyBreakDate) it.apply {
+ isRead = false
+ if (notify) isNotified = false
+ }
+ },
+ )
}
private suspend fun refreshGradeSummaries(
@@ -115,31 +120,43 @@ class GradeRepository @Inject constructor(
newSummary: List,
notify: Boolean
) {
- gradeSummaryDb.deleteAll(oldSummaries uniqueSubtract newSummary)
- gradeSummaryDb.insertAll((newSummary uniqueSubtract oldSummaries).onEach { summary ->
- val oldSummary = oldSummaries.find { old -> old.subject == summary.subject }
- summary.isPredictedGradeNotified = when {
- summary.predictedGrade.isEmpty() -> true
- notify && oldSummary?.predictedGrade != summary.predictedGrade -> false
- else -> true
- }
- summary.isFinalGradeNotified = when {
- summary.finalGrade.isEmpty() -> true
- notify && oldSummary?.finalGrade != summary.finalGrade -> false
- else -> true
- }
+ gradeSummaryDb.removeOldAndSaveNew(
+ oldItems = oldSummaries uniqueSubtract newSummary,
+ newItems = (newSummary uniqueSubtract oldSummaries).onEach { summary ->
+ getGradeSummaryWithUpdatedNotificationState(
+ summary = summary,
+ oldSummary = oldSummaries.find { it.subject == summary.subject },
+ notify = notify,
+ )
+ },
+ )
+ }
- summary.predictedGradeLastChange = when {
- oldSummary == null -> Instant.now()
- summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
- else -> oldSummary.predictedGradeLastChange
- }
- summary.finalGradeLastChange = when {
- oldSummary == null -> Instant.now()
- summary.finalGrade != oldSummary.finalGrade -> Instant.now()
- else -> oldSummary.finalGradeLastChange
- }
- })
+ private fun getGradeSummaryWithUpdatedNotificationState(
+ summary: GradeSummary,
+ oldSummary: GradeSummary?,
+ notify: Boolean,
+ ) {
+ summary.isPredictedGradeNotified = when {
+ summary.predictedGrade.isEmpty() -> true
+ notify && oldSummary?.predictedGrade != summary.predictedGrade -> false
+ else -> true
+ }
+ summary.isFinalGradeNotified = when {
+ summary.finalGrade.isEmpty() -> true
+ notify && oldSummary?.finalGrade != summary.finalGrade -> false
+ else -> true
+ }
+ summary.predictedGradeLastChange = when {
+ oldSummary == null -> Instant.now()
+ summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
+ else -> oldSummary.predictedGradeLastChange
+ }
+ summary.finalGradeLastChange = when {
+ oldSummary == null -> Instant.now()
+ summary.finalGrade != oldSummary.finalGrade -> Instant.now()
+ else -> oldSummary.finalGradeLastChange
+ }
}
fun getUnreadGrades(semester: Semester): Flow> {
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
index 23d7b858..809f92d3 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt
@@ -19,7 +19,7 @@ import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.switchSemester
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.sync.Mutex
-import java.util.*
+import java.util.Locale
import javax.inject.Inject
import javax.inject.Singleton
@@ -62,8 +62,10 @@ class GradeStatisticsRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
- gradePartialStatisticsDb.deleteAll(old uniqueSubtract new)
- gradePartialStatisticsDb.insertAll(new uniqueSubtract old)
+ gradePartialStatisticsDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = new uniqueSubtract old,
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(partialCacheKey, semester))
},
mapResult = { items ->
@@ -80,6 +82,7 @@ class GradeStatisticsRepository @Inject constructor(
)
listOf(summaryItem) + items
}
+
else -> items.filter { it.subject == subjectName }
}.mapPartialToStatisticItems()
}
@@ -107,8 +110,10 @@ class GradeStatisticsRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
- gradeSemesterStatisticsDb.deleteAll(old uniqueSubtract new)
- gradeSemesterStatisticsDb.insertAll(new uniqueSubtract old)
+ gradeSemesterStatisticsDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = new uniqueSubtract old,
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(semesterCacheKey, semester))
},
mapResult = { items ->
@@ -138,6 +143,7 @@ class GradeStatisticsRepository @Inject constructor(
}
listOf(summaryItem) + itemsWithAverage
}
+
else -> itemsWithAverage.filter { it.subject == subjectName }
}.mapSemesterToStatisticItems()
}
@@ -163,8 +169,10 @@ class GradeStatisticsRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
- gradePointsStatisticsDb.deleteAll(old uniqueSubtract new)
- gradePointsStatisticsDb.insertAll(new uniqueSubtract old)
+ gradePointsStatisticsDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = new uniqueSubtract old,
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(pointsCacheKey, semester))
},
mapResult = { items ->
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
index 010cf845..1a9c7ffa 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt
@@ -61,14 +61,14 @@ class HomeworkRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
- val homeWorkToSave = (new uniqueSubtract old).onEach {
- if (notify) it.isNotified = false
- }
val filteredOld = old.filterNot { it.isAddedByUser }
- homeworkDb.deleteAll(filteredOld uniqueSubtract new)
- homeworkDb.insertAll(homeWorkToSave)
-
+ homeworkDb.removeOldAndSaveNew(
+ oldItems = filteredOld uniqueSubtract new,
+ newItems = (new uniqueSubtract old).onEach {
+ if (notify) it.isNotified = false
+ },
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
}
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt
index 4ff4517d..45b7f6e2 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt
@@ -18,7 +18,7 @@ import javax.inject.Singleton
@Singleton
class LuckyNumberRepository @Inject constructor(
private val luckyNumberDb: LuckyNumberDao,
- private val sdk: Sdk
+ private val sdk: Sdk,
) {
private val saveFetchResultMutex = Mutex()
@@ -39,11 +39,10 @@ class LuckyNumberRepository @Inject constructor(
newLuckyNumber ?: return@networkBoundResource
if (newLuckyNumber != oldLuckyNumber) {
- val updatedLuckNumberList =
- listOf(newLuckyNumber.apply { if (notify) isNotified = false })
-
- oldLuckyNumber?.let { luckyNumberDb.deleteAll(listOfNotNull(it)) }
- luckyNumberDb.insertAll(updatedLuckNumberList)
+ luckyNumberDb.removeOldAndSaveNew(
+ oldItems = listOfNotNull(oldLuckyNumber),
+ newItems = listOf(newLuckyNumber.apply { if (notify) isNotified = false }),
+ )
}
}
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
index 96f04870..a4517760 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt
@@ -89,12 +89,13 @@ class MessageRepository @Inject constructor(
},
saveFetchResult = { oldWithAuthors, new ->
val old = oldWithAuthors.map { it.message }
- messagesDb.deleteAll(old uniqueSubtract new)
- messagesDb.insertAll((new uniqueSubtract old).onEach {
- val muted = isMuted(it.correspondents)
- it.isNotified = !notify || muted
- })
-
+ messagesDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = (new uniqueSubtract old).onEach {
+ val muted = isMuted(it.correspondents)
+ it.isNotified = !notify || muted
+ },
+ )
refreshHelper.updateLastRefreshTimestamp(
getRefreshKey(messagesCacheKey, mailbox, folder)
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
index 412f9e7f..48b4fc28 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt
@@ -48,9 +48,10 @@ class MobileDeviceRepository @Inject constructor(
.mapToEntities(student)
},
saveFetchResult = { old, new ->
- mobileDb.deleteAll(old uniqueSubtract new)
- mobileDb.insertAll(new uniqueSubtract old)
-
+ mobileDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = new uniqueSubtract old,
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
index eeb1d53e..feb92c15 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt
@@ -7,7 +7,12 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.*
+import io.github.wulkanowy.utils.AutoRefreshHelper
+import io.github.wulkanowy.utils.getRefreshKey
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.switchSemester
+import io.github.wulkanowy.utils.toLocalDate
+import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
@@ -46,14 +51,16 @@ class NoteRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
- noteDb.deleteAll(old uniqueSubtract new)
- noteDb.insertAll((new uniqueSubtract old).onEach {
+ val notesToAdd = (new uniqueSubtract old).onEach {
if (it.date >= student.registrationDate.toLocalDate()) it.apply {
isRead = false
if (notify) isNotified = false
}
- })
-
+ }
+ noteDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = notesToAdd,
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt
index 79984ce6..4a1474ce 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt
@@ -1,7 +1,11 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.dao.RecipientDao
-import io.github.wulkanowy.data.db.entities.*
+import io.github.wulkanowy.data.db.entities.Mailbox
+import io.github.wulkanowy.data.db.entities.MailboxType
+import io.github.wulkanowy.data.db.entities.Message
+import io.github.wulkanowy.data.db.entities.Recipient
+import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
@@ -25,8 +29,10 @@ class RecipientRepository @Inject constructor(
.mapToEntities(mailbox.globalKey)
val old = recipientDb.loadAll(type, mailbox.globalKey)
- recipientDb.deleteAll(old uniqueSubtract new)
- recipientDb.insertAll(new uniqueSubtract old)
+ recipientDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = new uniqueSubtract old,
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
index 8537fbc3..f09a46aa 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt
@@ -47,12 +47,12 @@ class SchoolAnnouncementRepository @Inject constructor(
lastAnnouncements + directorInformation
},
saveFetchResult = { old, new ->
- val schoolAnnouncementsToSave = (new uniqueSubtract old).onEach {
- if (notify) it.isNotified = false
- }
-
- schoolAnnouncementDb.deleteAll(old uniqueSubtract new)
- schoolAnnouncementDb.insertAll(schoolAnnouncementsToSave)
+ schoolAnnouncementDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = (new uniqueSubtract old).onEach {
+ if (notify) it.isNotified = false
+ },
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
index f757ef04..b42b4d57 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt
@@ -47,10 +47,10 @@ class SchoolRepository @Inject constructor(
},
saveFetchResult = { old, new ->
if (old != null && new != old) {
- with(schoolDb) {
- deleteAll(listOf(old))
- insertAll(listOf(new))
- }
+ schoolDb.removeOldAndSaveNew(
+ oldItems = listOf(old),
+ newItems = listOf(new)
+ )
} else if (old == null) {
schoolDb.insertAll(listOf(new))
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt
index dd44df70..9ae22bab 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt
@@ -5,7 +5,11 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
-import io.github.wulkanowy.utils.*
+import io.github.wulkanowy.utils.DispatchersProvider
+import io.github.wulkanowy.utils.getCurrentOrLast
+import io.github.wulkanowy.utils.init
+import io.github.wulkanowy.utils.isCurrent
+import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
@@ -15,7 +19,7 @@ import javax.inject.Singleton
class SemesterRepository @Inject constructor(
private val semesterDb: SemesterDao,
private val sdk: Sdk,
- private val dispatchers: DispatchersProvider
+ private val dispatchers: DispatchersProvider,
) {
suspend fun getSemesters(
@@ -45,6 +49,7 @@ class SemesterRepository @Inject constructor(
0 == it.diaryId && 0 == it.kindergartenDiaryId
} == true
}
+
else -> false
}
@@ -59,8 +64,10 @@ class SemesterRepository @Inject constructor(
if (new.isEmpty()) return Timber.i("Empty semester list!")
val old = semesterDb.loadAll(student.studentId, student.classId)
- semesterDb.deleteAll(old.uniqueSubtract(new))
- semesterDb.insertSemesters(new.uniqueSubtract(old))
+ semesterDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = new uniqueSubtract old,
+ )
}
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) =
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
index d6cd25c8..d42be180 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt
@@ -15,7 +15,7 @@ import javax.inject.Singleton
@Singleton
class StudentInfoRepository @Inject constructor(
private val studentInfoDao: StudentInfoDao,
- private val sdk: Sdk
+ private val sdk: Sdk,
) {
private val saveFetchResultMutex = Mutex()
@@ -36,10 +36,10 @@ class StudentInfoRepository @Inject constructor(
},
saveFetchResult = { old, new ->
if (old != null && new != old) {
- with(studentInfoDao) {
- deleteAll(listOf(old))
- insertAll(listOf(new))
- }
+ studentInfoDao.removeOldAndSaveNew(
+ oldItems = listOf(old),
+ newItems = listOf(new),
+ )
} else if (old == null) {
studentInfoDao.insertAll(listOf(new))
}
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
index 98cb181a..cf7f86c2 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt
@@ -45,9 +45,10 @@ class SubjectRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
- subjectDao.deleteAll(old uniqueSubtract new)
- subjectDao.insertAll(new uniqueSubtract old)
-
+ subjectDao.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = new uniqueSubtract old
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
index 42698f92..5a488b27 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt
@@ -45,9 +45,10 @@ class TeacherRepository @Inject constructor(
.mapToEntities(semester)
},
saveFetchResult = { old, new ->
- teacherDb.deleteAll(old uniqueSubtract new)
- teacherDb.insertAll(new uniqueSubtract old)
-
+ teacherDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = new uniqueSubtract old,
+ )
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)
diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
index acbd02d1..0d208c1f 100644
--- a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt
@@ -154,8 +154,10 @@ class TimetableRepository @Inject constructor(
new.apply { if (notify) isNotified = false }
}
- timetableDb.deleteAll(lessonsToRemove)
- timetableDb.insertAll(lessonsToAdd)
+ timetableDb.removeOldAndSaveNew(
+ oldItems = lessonsToRemove,
+ newItems = lessonsToAdd,
+ )
schedulerHelper.cancelScheduled(lessonsToRemove, student)
schedulerHelper.scheduleNotifications(lessonsToAdd, student)
@@ -166,13 +168,17 @@ class TimetableRepository @Inject constructor(
new: List
) {
val oldFiltered = old.filter { !it.isAddedByUser }
- timetableAdditionalDb.deleteAll(oldFiltered uniqueSubtract new)
- timetableAdditionalDb.insertAll(new uniqueSubtract old)
+ timetableAdditionalDb.removeOldAndSaveNew(
+ oldItems = oldFiltered uniqueSubtract new,
+ newItems = new uniqueSubtract old,
+ )
}
private suspend fun refreshDayHeaders(old: List, new: List) {
- timetableHeaderDb.deleteAll(old uniqueSubtract new)
- timetableHeaderDb.insertAll(new uniqueSubtract old)
+ timetableHeaderDb.removeOldAndSaveNew(
+ oldItems = old uniqueSubtract new,
+ newItems = new uniqueSubtract old,
+ )
}
fun getLastRefreshTimestamp(semester: Semester, start: LocalDate, end: LocalDate): Instant {
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
index d0e500f1..e64144c2 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt
@@ -10,11 +10,17 @@ import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.mockk.*
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
+import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -61,26 +67,36 @@ class AttendanceRepositoryTest {
}
@Test
- fun `force refresh without difference`() {
+ fun `force refresh without difference`() = runTest {
// prepare
coEvery { sdk.getAttendance(startDate, endDate) } returns remoteList
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester, emptyList())),
flowOf(remoteList.mapToEntities(semester, emptyList()))
)
- coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { attendanceDb.deleteAll(any()) } just Runs
+ coEvery { attendanceDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { attendanceRepository.getAttendance(student, semester, startDate, endDate, true).toFirstResult() }
+ val res = attendanceRepository.getAttendance(
+ student = student,
+ semester = semester,
+ start = startDate,
+ end = endDate,
+ forceRefresh = true,
+ ).toFirstResult()
+
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getAttendance(startDate, endDate) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
- coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
- coVerify { attendanceDb.deleteAll(match { it.isEmpty() }) }
+ coVerify {
+ attendanceDb.removeOldAndSaveNew(
+ oldItems = match { it.isEmpty() },
+ newItems = match { it.isEmpty() },
+ )
+ }
}
@Test
@@ -89,14 +105,23 @@ class AttendanceRepositoryTest {
coEvery { sdk.getAttendance(startDate, endDate) } returns remoteList
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())),
- flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList())), // after fetch end before save result
+ flowOf(
+ remoteList.dropLast(1).mapToEntities(semester, emptyList())
+ ), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester, emptyList()))
)
- coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { attendanceDb.deleteAll(any()) } just Runs
+ coEvery { attendanceDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { attendanceRepository.getAttendance(student, semester, startDate, endDate, true).toFirstResult() }
+ val res = runBlocking {
+ attendanceRepository.getAttendance(
+ student,
+ semester,
+ startDate,
+ endDate,
+ true
+ ).toFirstResult()
+ }
// verify
assertEquals(null, res.errorOrNull)
@@ -104,11 +129,13 @@ class AttendanceRepositoryTest {
coVerify { sdk.getAttendance(startDate, endDate) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
coVerify {
- attendanceDb.insertAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
- })
+ attendanceDb.removeOldAndSaveNew(
+ oldItems = match { it.isEmpty() },
+ newItems = match {
+ it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
+ },
+ )
}
- coVerify { attendanceDb.deleteAll(match { it.isEmpty() }) }
}
@Test
@@ -117,25 +144,39 @@ class AttendanceRepositoryTest {
coEvery { sdk.getAttendance(startDate, endDate) } returns remoteList.dropLast(1)
coEvery { attendanceDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester, emptyList())),
- flowOf(remoteList.mapToEntities(semester, emptyList())), // after fetch end before save result
+ flowOf(
+ remoteList.mapToEntities(
+ semester,
+ emptyList()
+ )
+ ), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(semester, emptyList()))
)
- coEvery { attendanceDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { attendanceDb.deleteAll(any()) } just Runs
+ coEvery { attendanceDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { attendanceRepository.getAttendance(student, semester, startDate, endDate, true).toFirstResult() }
+ val res = runBlocking {
+ attendanceRepository.getAttendance(
+ student,
+ semester,
+ startDate,
+ endDate,
+ true
+ ).toFirstResult()
+ }
// verify
assertEquals(null, res.errorOrNull)
assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getAttendance(startDate, endDate) }
coVerify { attendanceDb.loadAll(1, 1, startDate, endDate) }
- coVerify { attendanceDb.insertAll(match { it.isEmpty() }) }
coVerify {
- attendanceDb.deleteAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
- })
+ attendanceDb.removeOldAndSaveNew(
+ oldItems = match {
+ it.size == 1 && it[0] == remoteList.mapToEntities(semester, emptyList())[1]
+ },
+ newItems = emptyList(),
+ )
}
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt
index c28ea304..f8f68850 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt
@@ -9,11 +9,16 @@ import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.mockk.*
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
+import io.mockk.just
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -52,46 +57,28 @@ class CompletedLessonsRepositoryTest {
MockKAnnotations.init(this)
every { refreshHelper.shouldBeRefreshed(any()) } returns false
- completedLessonRepository = CompletedLessonsRepository(completedLessonDb, sdk, refreshHelper)
+ completedLessonRepository =
+ CompletedLessonsRepository(completedLessonDb, sdk, refreshHelper)
}
@Test
- fun `force refresh without difference`() {
+ fun `force refresh without difference`() = runTest {
// prepare
coEvery { sdk.getCompletedLessons(startDate, endDate) } returns remoteList
coEvery { completedLessonDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester))
)
- coEvery { completedLessonDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { completedLessonDb.deleteAll(any()) } just Runs
+ coEvery { completedLessonDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { completedLessonRepository.getCompletedLessons(student, semester, startDate, endDate, true).toFirstResult() }
-
- // verify
- assertEquals(null, res.errorOrNull)
- assertEquals(2, res.dataOrNull?.size)
- coVerify { sdk.getCompletedLessons(startDate, endDate) }
- coVerify { completedLessonDb.loadAll(1, 1, startDate, endDate) }
- coVerify { completedLessonDb.insertAll(match { it.isEmpty() }) }
- coVerify { completedLessonDb.deleteAll(match { it.isEmpty() }) }
- }
-
- @Test
- fun `force refresh with more items in remote`() {
- // prepare
- coEvery { sdk.getCompletedLessons(startDate, endDate) } returns remoteList
- coEvery { completedLessonDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
- flowOf(remoteList.dropLast(1).mapToEntities(semester)),
- flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
- flowOf(remoteList.mapToEntities(semester))
- )
- coEvery { completedLessonDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { completedLessonDb.deleteAll(any()) } just Runs
-
- // execute
- val res = runBlocking { completedLessonRepository.getCompletedLessons(student, semester, startDate, endDate, true).toFirstResult() }
+ val res = completedLessonRepository.getCompletedLessons(
+ student = student,
+ semester = semester,
+ start = startDate,
+ end = endDate,
+ forceRefresh = true,
+ ).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
@@ -99,15 +86,52 @@ class CompletedLessonsRepositoryTest {
coVerify { sdk.getCompletedLessons(startDate, endDate) }
coVerify { completedLessonDb.loadAll(1, 1, startDate, endDate) }
coVerify {
- completedLessonDb.insertAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
- })
+ completedLessonDb.removeOldAndSaveNew(
+ oldItems = match { it.isEmpty() },
+ newItems = match { it.isEmpty() },
+ )
}
- coVerify { completedLessonDb.deleteAll(match { it.isEmpty() }) }
}
@Test
- fun `force refresh with more items in local`() {
+ fun `force refresh with more items in remote`() = runTest {
+ // prepare
+ coEvery { sdk.getCompletedLessons(startDate, endDate) } returns remoteList
+ coEvery { completedLessonDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
+ flowOf(remoteList.dropLast(1).mapToEntities(semester)),
+ flowOf(
+ remoteList.dropLast(1).mapToEntities(semester)
+ ), // after fetch end before save result
+ flowOf(remoteList.mapToEntities(semester))
+ )
+ coEvery { completedLessonDb.removeOldAndSaveNew(any(), any()) } just Runs
+
+ // execute
+ val res = completedLessonRepository.getCompletedLessons(
+ student = student,
+ semester = semester,
+ start = startDate,
+ end = endDate,
+ forceRefresh = true
+ ).toFirstResult()
+
+ // verify
+ assertEquals(null, res.errorOrNull)
+ assertEquals(2, res.dataOrNull?.size)
+ coVerify { sdk.getCompletedLessons(startDate, endDate) }
+ coVerify { completedLessonDb.loadAll(1, 1, startDate, endDate) }
+ coVerify {
+ completedLessonDb.removeOldAndSaveNew(
+ oldItems = match { it.isEmpty() },
+ newItems = match {
+ it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
+ }
+ )
+ }
+ }
+
+ @Test
+ fun `force refresh with more items in local`() = runTest {
// prepare
coEvery { sdk.getCompletedLessons(startDate, endDate) } returns remoteList.dropLast(1)
coEvery { completedLessonDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
@@ -115,22 +139,29 @@ class CompletedLessonsRepositoryTest {
flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(semester))
)
- coEvery { completedLessonDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { completedLessonDb.deleteAll(any()) } just Runs
+ coEvery { completedLessonDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { completedLessonRepository.getCompletedLessons(student, semester, startDate, endDate, true).toFirstResult() }
+ val res = completedLessonRepository.getCompletedLessons(
+ student = student,
+ semester = semester,
+ start = startDate,
+ end = endDate,
+ forceRefresh = true,
+ ).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getCompletedLessons(startDate, endDate) }
coVerify { completedLessonDb.loadAll(1, 1, startDate, endDate) }
- coVerify { completedLessonDb.insertAll(match { it.isEmpty() }) }
coVerify {
- completedLessonDb.deleteAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
- })
+ completedLessonDb.removeOldAndSaveNew(
+ oldItems = match {
+ it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
+ },
+ newItems = match { it.isEmpty() },
+ )
}
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt
index fb037a87..d1ed9ca3 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt
@@ -9,11 +9,17 @@ import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
-import io.mockk.*
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
+import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -64,35 +70,42 @@ class ExamRemoteTest {
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester))
)
- coEvery { examDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { examDb.deleteAll(any()) } just Runs
+ coEvery { examDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { examRepository.getExams(student, semester, startDate, endDate, true).toFirstResult() }
+ val res = runBlocking {
+ examRepository.getExams(student, semester, startDate, endDate, true).toFirstResult()
+ }
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.size)
coVerify { sdk.getExams(startDate, realEndDate) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
- coVerify { examDb.insertAll(match { it.isEmpty() }) }
- coVerify { examDb.deleteAll(match { it.isEmpty() }) }
+ coVerify { examDb.removeOldAndSaveNew(emptyList(), emptyList()) }
}
@Test
- fun `force refresh with more items in remote`() {
+ fun `force refresh with more items in remote`() = runTest {
// prepare
coEvery { sdk.getExams(startDate, realEndDate) } returns remoteList
coEvery { examDb.loadAll(1, 1, startDate, realEndDate) } returnsMany listOf(
flowOf(remoteList.dropLast(1).mapToEntities(semester)),
- flowOf(remoteList.dropLast(1).mapToEntities(semester)), // after fetch end before save result
+ flowOf(
+ remoteList.dropLast(1).mapToEntities(semester)
+ ), // after fetch end before save result
flowOf(remoteList.mapToEntities(semester))
)
- coEvery { examDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { examDb.deleteAll(any()) } just Runs
+ coEvery { examDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { examRepository.getExams(student, semester, startDate, endDate, true).toFirstResult() }
+ val res = examRepository.getExams(
+ student = student,
+ semester = semester,
+ start = startDate,
+ end = endDate,
+ forceRefresh = true,
+ ).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
@@ -100,15 +113,17 @@ class ExamRemoteTest {
coVerify { sdk.getExams(startDate, realEndDate) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
coVerify {
- examDb.insertAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
- })
+ examDb.removeOldAndSaveNew(
+ oldItems = emptyList(),
+ newItems = match {
+ it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
+ },
+ )
}
- coVerify { examDb.deleteAll(match { it.isEmpty() }) }
}
@Test
- fun `force refresh with more items in local`() {
+ fun `force refresh with more items in local`() = runTest {
// prepare
coEvery { sdk.getExams(startDate, realEndDate) } returns remoteList.dropLast(1)
coEvery { examDb.loadAll(1, 1, startDate, realEndDate) } returnsMany listOf(
@@ -116,22 +131,27 @@ class ExamRemoteTest {
flowOf(remoteList.mapToEntities(semester)), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(semester))
)
- coEvery { examDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { examDb.deleteAll(any()) } just Runs
+ coEvery { examDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { examRepository.getExams(student, semester, startDate, endDate, true).toFirstResult() }
+ val res = examRepository.getExams(
+ student = student,
+ semester = semester,
+ start = startDate,
+ end = endDate,
+ forceRefresh = true,
+ ).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getExams(startDate, realEndDate) }
coVerify { examDb.loadAll(1, 1, startDate, realEndDate) }
- coVerify { examDb.insertAll(match { it.isEmpty() }) }
coVerify {
- examDb.deleteAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1]
- })
+ examDb.removeOldAndSaveNew(
+ oldItems = match { it.size == 1 && it[0] == remoteList.mapToEntities(semester)[1] },
+ newItems = emptyList()
+ )
}
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
index 515b0d66..0ea5d3fa 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt
@@ -22,6 +22,7 @@ import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -60,26 +61,27 @@ class GradeRepositoryTest {
MockKAnnotations.init(this)
every { refreshHelper.shouldBeRefreshed(any()) } returns false
- gradeRepository =
- GradeRepository(gradeDb, gradeSummaryDb, gradeDescriptiveDb, sdk, refreshHelper)
+ gradeRepository = GradeRepository(
+ gradeDb = gradeDb,
+ gradeSummaryDb = gradeSummaryDb,
+ gradeDescriptiveDb = gradeDescriptiveDb,
+ sdk = sdk,
+ refreshHelper = refreshHelper,
+ )
- coEvery { gradeDb.deleteAll(any()) } just Runs
- coEvery { gradeDb.insertAll(any()) } returns listOf()
+ coEvery { gradeDb.removeOldAndSaveNew(any(), any()) } just Runs
+ coEvery { gradeSummaryDb.removeOldAndSaveNew(any(), any()) } just Runs
coEvery { gradeSummaryDb.loadAll(1, 1) } returnsMany listOf(
flowOf(listOf()),
flowOf(listOf()),
flowOf(listOf())
)
- coEvery { gradeSummaryDb.deleteAll(any()) } just Runs
- coEvery { gradeSummaryDb.insertAll(any()) } returns listOf()
+ coEvery { gradeDescriptiveDb.removeOldAndSaveNew(any(), any()) } just Runs
coEvery { gradeDescriptiveDb.loadAll(any(), any()) } returnsMany listOf(
flowOf(listOf()),
)
-
- coEvery { gradeDescriptiveDb.deleteAll(any()) } just Runs
- coEvery { gradeDescriptiveDb.insertAll(any()) } returns listOf()
}
@Test
@@ -113,13 +115,16 @@ class GradeRepositoryTest {
assertEquals(null, res.errorOrNull)
assertEquals(4, res.dataOrNull?.first?.size)
coVerify {
- gradeDb.insertAll(withArg {
- assertEquals(4, it.size)
- assertTrue(it[0].isRead)
- assertTrue(it[1].isRead)
- assertFalse(it[2].isRead)
- assertFalse(it[3].isRead)
- })
+ gradeDb.removeOldAndSaveNew(
+ oldItems = emptyList(),
+ newItems = withArg {
+ assertEquals(4, it.size)
+ assertTrue(it[0].isRead)
+ assertTrue(it[1].isRead)
+ assertFalse(it[2].isRead)
+ assertFalse(it[3].isRead)
+ },
+ )
}
}
@@ -167,23 +172,23 @@ class GradeRepositoryTest {
assertEquals(null, res.errorOrNull)
assertEquals(4, res.dataOrNull?.first?.size)
coVerify {
- gradeDb.insertAll(withArg {
- assertEquals(3, it.size)
- assertTrue(it[0].isRead)
- assertTrue(it[1].isRead)
- assertFalse(it[2].isRead)
- assertEquals(remoteList.mapToEntities(semester).last(), it[2])
- })
- }
- coVerify {
- gradeDb.deleteAll(withArg {
- assertEquals(2, it.size)
- })
+ gradeDb.removeOldAndSaveNew(
+ oldItems = withArg {
+ assertEquals(2, it.size)
+ },
+ newItems = withArg {
+ assertEquals(3, it.size)
+ assertTrue(it[0].isRead)
+ assertTrue(it[1].isRead)
+ assertFalse(it[2].isRead)
+ assertEquals(remoteList.mapToEntities(semester).last(), it[2])
+ }
+ )
}
}
@Test
- fun `force refresh when local contains duplicated grades`() {
+ fun `force refresh when local contains duplicated grades`() = runTest {
// prepare
val remoteList = listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
@@ -203,13 +208,17 @@ class GradeRepositoryTest {
)
// execute
- val res = runBlocking { gradeRepository.getGrades(student, semester, true).toFirstResult() }
+ val res = gradeRepository.getGrades(student, semester, true).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
assertEquals(2, res.dataOrNull?.first?.size)
- coVerify { gradeDb.insertAll(match { it.isEmpty() }) }
- coVerify { gradeDb.deleteAll(match { it.size == 1 }) } // ... here
+ coVerify {
+ gradeDb.removeOldAndSaveNew(
+ oldItems = match { it.size == 1 }, // ... here
+ newItems = emptyList()
+ )
+ }
}
@Test
@@ -238,8 +247,12 @@ class GradeRepositoryTest {
// verify
assertEquals(null, res.errorOrNull)
assertEquals(3, res.dataOrNull?.first?.size)
- coVerify { gradeDb.insertAll(match { it.size == 1 }) } // ... here
- coVerify { gradeDb.deleteAll(match { it.isEmpty() }) }
+ coVerify {
+ gradeDb.removeOldAndSaveNew(
+ oldItems = emptyList(),
+ newItems = match { it.size == 1 }, // ... here
+ )
+ }
}
@Test
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt
index 8e2f7c6e..dfd36ee1 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt
@@ -71,8 +71,7 @@ class GradeStatisticsRepositoryTest {
flowOf(remotePartialList.mapToEntities(semester)),
flowOf(remotePartialList.mapToEntities(semester))
)
- coEvery { gradePartialStatisticsDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { gradePartialStatisticsDb.deleteAll(any()) } just Runs
+ coEvery { gradePartialStatisticsDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking {
@@ -93,8 +92,7 @@ class GradeStatisticsRepositoryTest {
assertEquals("", items[2].partial?.studentAverage)
coVerify { sdk.getGradesPartialStatistics(1) }
coVerify { gradePartialStatisticsDb.loadAll(1, 1) }
- coVerify { gradePartialStatisticsDb.insertAll(match { it.isEmpty() }) }
- coVerify { gradePartialStatisticsDb.deleteAll(match { it.isEmpty() }) }
+ coVerify { gradePartialStatisticsDb.removeOldAndSaveNew(emptyList(), emptyList()) }
}
@Test
@@ -109,8 +107,7 @@ class GradeStatisticsRepositoryTest {
flowOf(remotePartialList.mapToEntities(semester)),
flowOf(remotePartialList.mapToEntities(semester))
)
- coEvery { gradePartialStatisticsDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { gradePartialStatisticsDb.deleteAll(any()) } just Runs
+ coEvery { gradePartialStatisticsDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking {
@@ -131,8 +128,7 @@ class GradeStatisticsRepositoryTest {
assertEquals("5.0", items[2].partial?.studentAverage)
coVerify { sdk.getGradesPartialStatistics(1) }
coVerify { gradePartialStatisticsDb.loadAll(1, 1) }
- coVerify { gradePartialStatisticsDb.insertAll(match { it.isEmpty() }) }
- coVerify { gradePartialStatisticsDb.deleteAll(match { it.isEmpty() }) }
+ coVerify { gradePartialStatisticsDb.removeOldAndSaveNew(emptyList(), emptyList()) }
}
private fun getGradeStatisticsPartialSubject(
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt
index 3225c3bd..fa78b1bd 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/LuckyNumberRemoteTest.kt
@@ -7,11 +7,16 @@ import io.github.wulkanowy.data.mappers.mapToEntity
import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
-import io.mockk.*
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerify
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
+import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -53,7 +58,8 @@ class LuckyNumberRemoteTest {
coEvery { luckyNumberDb.deleteAll(any()) } just Runs
// execute
- val res = runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
+ val res =
+ runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
// verify
assertEquals(null, res.errorOrNull)
@@ -65,19 +71,19 @@ class LuckyNumberRemoteTest {
}
@Test
- fun `force refresh with different item on remote`() {
+ fun `force refresh with different item on remote`() = runTest {
// prepare
coEvery { sdk.getLuckyNumber(student.schoolShortName) } returns luckyNumber
coEvery { luckyNumberDb.load(1, date) } returnsMany listOf(
flowOf(luckyNumber.mapToEntity(student).copy(luckyNumber = 6666)),
- flowOf(luckyNumber.mapToEntity(student).copy(luckyNumber = 6666)), // after fetch end before save result
+ // after fetch end before save result
+ flowOf(luckyNumber.mapToEntity(student).copy(luckyNumber = 6666)),
flowOf(luckyNumber.mapToEntity(student))
)
- coEvery { luckyNumberDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { luckyNumberDb.deleteAll(any()) } just Runs
+ coEvery { luckyNumberDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
+ val res = luckyNumberRepository.getLuckyNumber(student, true).toFirstResult()
// verify
assertEquals(null, res.errorOrNull)
@@ -85,13 +91,16 @@ class LuckyNumberRemoteTest {
coVerify { sdk.getLuckyNumber(student.schoolShortName) }
coVerify { luckyNumberDb.load(1, date) }
coVerify {
- luckyNumberDb.insertAll(match {
- it.size == 1 && it[0] == luckyNumber.mapToEntity(student)
- })
+ luckyNumberDb.removeOldAndSaveNew(
+ oldItems = match {
+ it.size == 1 && it[0] == luckyNumber.mapToEntity(student)
+ .copy(luckyNumber = 6666)
+ },
+ newItems = match {
+ it.size == 1 && it[0] == luckyNumber.mapToEntity(student)
+ }
+ )
}
- coVerify { luckyNumberDb.deleteAll(match {
- it.size == 1 && it[0] == luckyNumber.mapToEntity(student).copy(luckyNumber = 6666)
- }) }
}
@Test
@@ -103,11 +112,11 @@ class LuckyNumberRemoteTest {
flowOf(null), // after fetch end before save result
flowOf(luckyNumber.mapToEntity(student))
)
- coEvery { luckyNumberDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { luckyNumberDb.deleteAll(any()) } just Runs
+ coEvery { luckyNumberDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
+ val res =
+ runBlocking { luckyNumberRepository.getLuckyNumber(student, true).toFirstResult() }
// verify
assertEquals(null, res.errorOrNull)
@@ -115,10 +124,12 @@ class LuckyNumberRemoteTest {
coVerify { sdk.getLuckyNumber(student.schoolShortName) }
coVerify { luckyNumberDb.load(1, date) }
coVerify {
- luckyNumberDb.insertAll(match {
- it.size == 1 && it[0] == luckyNumber.mapToEntity(student)
- })
+ luckyNumberDb.removeOldAndSaveNew(
+ oldItems = emptyList(),
+ newItems = match {
+ it.size == 1 && it[0] == luckyNumber.mapToEntity(student)
+ }
+ )
}
- coVerify(exactly = 0) { luckyNumberDb.deleteAll(any()) }
}
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
index 58937e77..fbbe4934 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt
@@ -113,7 +113,7 @@ class MessageRepositoryTest {
}
@Test
- fun `get messages when fetched completely new message without notify`() = runBlocking {
+ fun `get messages when fetched completely new message without notify`() = runTest {
coEvery { mailboxDao.loadAll(any()) } returns listOf(mailbox)
every { messageDb.loadAll(mailbox.globalKey, any()) } returns flowOf(emptyList())
coEvery { sdk.getMessages(Folder.RECEIVED, any()) } returns listOf(
@@ -122,8 +122,7 @@ class MessageRepositoryTest {
readBy = 10,
)
)
- coEvery { messageDb.deleteAll(any()) } just Runs
- coEvery { messageDb.insertAll(any()) } returns listOf()
+ coEvery { messageDb.removeOldAndSaveNew(any(), any()) } just Runs
val res = repository.getMessages(
student = student,
@@ -134,12 +133,14 @@ class MessageRepositoryTest {
).toFirstResult()
assertEquals(null, res.errorOrNull)
- coVerify(exactly = 1) { messageDb.deleteAll(withArg { checkEquals(emptyList()) }) }
coVerify {
- messageDb.insertAll(withArg {
- assertEquals(4, it.single().messageId)
- assertTrue(it.single().isNotified)
- })
+ messageDb.removeOldAndSaveNew(
+ oldItems = withArg { checkEquals(emptyList()) },
+ newItems = withArg {
+ assertEquals(4, it.single().messageId)
+ assertTrue(it.single().isNotified)
+ },
+ )
}
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
index 1a3f9679..aa93a5e6 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt
@@ -19,7 +19,7 @@ import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runTest
import org.junit.Assert
import org.junit.Before
import org.junit.Test
@@ -57,42 +57,21 @@ class MobileDeviceRepositoryTest {
}
@Test
- fun `force refresh without difference`() {
+ fun `force refresh without difference`() = runTest {
// prepare
coEvery { sdk.getRegisteredDevices() } returns remoteList
coEvery { mobileDeviceDb.loadAll(student.studentId) } returnsMany listOf(
flowOf(remoteList.mapToEntities(student)),
flowOf(remoteList.mapToEntities(student))
)
- coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { mobileDeviceDb.deleteAll(any()) } just Runs
+ coEvery { mobileDeviceDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { mobileDeviceRepository.getDevices(student, semester, true).toFirstResult() }
-
- // verify
- Assert.assertEquals(null, res.errorOrNull)
- Assert.assertEquals(2, res.dataOrNull?.size)
- coVerify { sdk.getRegisteredDevices() }
- coVerify { mobileDeviceDb.loadAll(1) }
- coVerify { mobileDeviceDb.insertAll(match { it.isEmpty() }) }
- coVerify { mobileDeviceDb.deleteAll(match { it.isEmpty() }) }
- }
-
- @Test
- fun `force refresh with more items in remote`() {
- // prepare
- coEvery { sdk.getRegisteredDevices() } returns remoteList
- coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf(
- flowOf(remoteList.dropLast(1).mapToEntities(student)),
- flowOf(remoteList.dropLast(1).mapToEntities(student)), // after fetch end before save result
- flowOf(remoteList.mapToEntities(student))
- )
- coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { mobileDeviceDb.deleteAll(any()) } just Runs
-
- // execute
- val res = runBlocking { mobileDeviceRepository.getDevices(student, semester, true).toFirstResult() }
+ val res = mobileDeviceRepository.getDevices(
+ student = student,
+ semester = semester,
+ forceRefresh = true,
+ ).toFirstResult()
// verify
Assert.assertEquals(null, res.errorOrNull)
@@ -100,15 +79,50 @@ class MobileDeviceRepositoryTest {
coVerify { sdk.getRegisteredDevices() }
coVerify { mobileDeviceDb.loadAll(1) }
coVerify {
- mobileDeviceDb.insertAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(student)[1]
- })
+ mobileDeviceDb.removeOldAndSaveNew(
+ oldItems = match { it.isEmpty() },
+ newItems = match { it.isEmpty() },
+ )
}
- coVerify { mobileDeviceDb.deleteAll(match { it.isEmpty() }) }
}
@Test
- fun `force refresh with more items in local`() {
+ fun `force refresh with more items in remote`() = runTest {
+ // prepare
+ coEvery { sdk.getRegisteredDevices() } returns remoteList
+ coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf(
+ flowOf(remoteList.dropLast(1).mapToEntities(student)),
+ flowOf(
+ remoteList.dropLast(1).mapToEntities(student)
+ ), // after fetch end before save result
+ flowOf(remoteList.mapToEntities(student))
+ )
+ coEvery { mobileDeviceDb.removeOldAndSaveNew(any(), any()) } just Runs
+
+ // execute
+ val res = mobileDeviceRepository.getDevices(
+ student = student,
+ semester = semester,
+ forceRefresh = true,
+ ).toFirstResult()
+
+ // verify
+ Assert.assertEquals(null, res.errorOrNull)
+ Assert.assertEquals(2, res.dataOrNull?.size)
+ coVerify { sdk.getRegisteredDevices() }
+ coVerify { mobileDeviceDb.loadAll(1) }
+ coVerify {
+ mobileDeviceDb.removeOldAndSaveNew(
+ oldItems = match { it.isEmpty() },
+ newItems = match {
+ it.size == 1 && it[0] == remoteList.mapToEntities(student)[1]
+ },
+ )
+ }
+ }
+
+ @Test
+ fun `force refresh with more items in local`() = runTest {
// prepare
coEvery { sdk.getRegisteredDevices() } returns remoteList.dropLast(1)
coEvery { mobileDeviceDb.loadAll(1) } returnsMany listOf(
@@ -116,22 +130,27 @@ class MobileDeviceRepositoryTest {
flowOf(remoteList.mapToEntities(student)), // after fetch end before save result
flowOf(remoteList.dropLast(1).mapToEntities(student))
)
- coEvery { mobileDeviceDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { mobileDeviceDb.deleteAll(any()) } just Runs
+ coEvery { mobileDeviceDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
- val res = runBlocking { mobileDeviceRepository.getDevices(student, semester, true).toFirstResult() }
+ val res = mobileDeviceRepository.getDevices(
+ student = student,
+ semester = semester,
+ forceRefresh = true,
+ ).toFirstResult()
// verify
Assert.assertEquals(null, res.errorOrNull)
Assert.assertEquals(1, res.dataOrNull?.size)
coVerify { sdk.getRegisteredDevices() }
coVerify { mobileDeviceDb.loadAll(1) }
- coVerify { mobileDeviceDb.insertAll(match { it.isEmpty() }) }
coVerify {
- mobileDeviceDb.deleteAll(match {
- it.size == 1 && it[0] == remoteList.mapToEntities(student)[1]
- })
+ mobileDeviceDb.removeOldAndSaveNew(
+ oldItems = match {
+ it.size == 1 && it[0] == remoteList.mapToEntities(student)[1]
+ },
+ newItems = match { it.isEmpty() },
+ )
}
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt
index ae73a795..e608cafb 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/RecipientLocalTest.kt
@@ -69,7 +69,12 @@ class RecipientLocalTest {
@Test
fun `load recipients when items already in database`() {
// prepare
- coEvery { recipientDb.loadAll(io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, "v4") } returnsMany listOf(
+ coEvery {
+ recipientDb.loadAll(
+ io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
+ "v4"
+ )
+ } returnsMany listOf(
remoteList.mapToEntities("v4"),
remoteList.mapToEntities("v4")
)
@@ -108,8 +113,7 @@ class RecipientLocalTest {
emptyList(),
remoteList.mapToEntities("v4")
)
- coEvery { recipientDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { recipientDb.deleteAll(any()) } just Runs
+ coEvery { recipientDb.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking {
@@ -123,8 +127,12 @@ class RecipientLocalTest {
// verify
assertEquals(3, res.size)
coVerify { sdk.getRecipients("v4") }
- coVerify { recipientDb.loadAll(io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN, "v4") }
- coVerify { recipientDb.insertAll(match { it.isEmpty() }) }
- coVerify { recipientDb.deleteAll(match { it.isEmpty() }) }
+ coVerify {
+ recipientDb.loadAll(
+ io.github.wulkanowy.data.db.entities.MailboxType.UNKNOWN,
+ "v4"
+ )
+ }
+ coVerify { recipientDb.removeOldAndSaveNew(match { it.isEmpty() }, match { it.isEmpty() }) }
}
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/SemesterRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/SemesterRepositoryTest.kt
index 31098d2e..96db8a79 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/SemesterRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/SemesterRepositoryTest.kt
@@ -50,13 +50,16 @@ class SemesterRepositoryTest {
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns emptyList()
coEvery { sdk.getSemesters() } returns semesters
- coEvery { semesterDb.deleteAll(any()) } just Runs
- coEvery { semesterDb.insertSemesters(any()) } returns emptyList()
+ coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
runBlocking { semesterRepository.getSemesters(student) }
- coVerify { semesterDb.insertSemesters(semesters.mapToEntities(student.studentId)) }
- coVerify { semesterDb.deleteAll(emptyList()) }
+ coVerify {
+ semesterDb.removeOldAndSaveNew(
+ oldItems = emptyList(),
+ newItems = semesters.mapToEntities(student.studentId),
+ )
+ }
}
@Test
@@ -71,12 +74,17 @@ class SemesterRepositoryTest {
getSemesterPojo(123, 2, now().minusMonths(3), now())
)
- coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns badSemesters.mapToEntities(student.studentId)
+ coEvery {
+ semesterDb.loadAll(
+ student.studentId,
+ student.classId
+ )
+ } returns badSemesters.mapToEntities(student.studentId)
coEvery { sdk.getSemesters() } returns goodSemesters
- coEvery { semesterDb.deleteAll(any()) } just Runs
- coEvery { semesterDb.insertSemesters(any()) } returns listOf()
+ coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
- val items = runBlocking { semesterRepository.getSemesters(student.copy(loginMode = Sdk.Mode.HEBE.name)) }
+ val items =
+ runBlocking { semesterRepository.getSemesters(student.copy(loginMode = Sdk.Mode.HEBE.name)) }
assertEquals(2, items.size)
assertEquals(0, items[0].diaryId)
}
@@ -99,8 +107,7 @@ class SemesterRepositoryTest {
goodSemesters.mapToEntities(student.studentId)
)
coEvery { sdk.getSemesters() } returns goodSemesters
- coEvery { semesterDb.deleteAll(any()) } just Runs
- coEvery { semesterDb.insertSemesters(any()) } returns listOf()
+ coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
val items = semesterRepository.getSemesters(
student = student.copy(loginMode = Sdk.Mode.SCRAPPER.name)
@@ -157,13 +164,16 @@ class SemesterRepositoryTest {
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns emptyList()
coEvery { sdk.getSemesters() } returns semesters
- coEvery { semesterDb.deleteAll(any()) } just Runs
- coEvery { semesterDb.insertSemesters(any()) } returns listOf()
+ coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
runBlocking { semesterRepository.getSemesters(student, refreshOnNoCurrent = true) }
- coVerify { semesterDb.deleteAll(emptyList()) }
- coVerify { semesterDb.insertSemesters(semesters.mapToEntities(student.studentId)) }
+ coVerify {
+ semesterDb.removeOldAndSaveNew(
+ oldItems = emptyList(),
+ newItems = semesters.mapToEntities(student.studentId),
+ )
+ }
}
@Test
@@ -181,12 +191,17 @@ class SemesterRepositoryTest {
getSemesterPojo(2, 2, now().plusMonths(5), now().plusMonths(11)),
)
- coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns semestersWithNoCurrent
+ coEvery {
+ semesterDb.loadAll(
+ student.studentId,
+ student.classId
+ )
+ } returns semestersWithNoCurrent
coEvery { sdk.getSemesters() } returns newSemesters
- coEvery { semesterDb.deleteAll(any()) } just Runs
- coEvery { semesterDb.insertSemesters(any()) } returns listOf()
+ coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
- val items = runBlocking { semesterRepository.getSemesters(student, refreshOnNoCurrent = true) }
+ val items =
+ runBlocking { semesterRepository.getSemesters(student, refreshOnNoCurrent = true) }
assertEquals(2, items.size)
}
diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
index 92ad01b1..2a61f99c 100644
--- a/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
+++ b/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt
@@ -108,8 +108,7 @@ class TimetableRepositoryTest {
flowOf(remoteList.mapToEntities(semester)),
flowOf(remoteList.mapToEntities(semester))
)
- coEvery { timetableDb.insertAll(any()) } returns listOf(1, 2, 3)
- coEvery { timetableDb.deleteAll(any()) } just Runs
+ coEvery { timetableDb.removeOldAndSaveNew(any(), any()) } just Runs
coEvery {
timetableAdditionalDao.loadAll(
@@ -119,12 +118,10 @@ class TimetableRepositoryTest {
end = endDate
)
} returns flowOf(listOf())
- coEvery { timetableAdditionalDao.deleteAll(emptyList()) } just Runs
- coEvery { timetableAdditionalDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
+ coEvery { timetableAdditionalDao.removeOldAndSaveNew(any(), any()) } just Runs
coEvery { timetableHeaderDao.loadAll(1, 1, startDate, endDate) } returns flowOf(listOf())
- coEvery { timetableHeaderDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
- coEvery { timetableHeaderDao.deleteAll(emptyList()) } just Runs
+ coEvery { timetableHeaderDao.removeOldAndSaveNew(any(), any()) } just Runs
// execute
val res = runBlocking {
@@ -142,8 +139,12 @@ class TimetableRepositoryTest {
assertEquals(2, res.dataOrNull!!.lessons.size)
coVerify { sdk.getTimetable(startDate, endDate) }
coVerify { timetableDb.loadAll(1, 1, startDate, endDate) }
- coVerify { timetableDb.insertAll(match { it.isEmpty() }) }
- coVerify { timetableDb.deleteAll(match { it.isEmpty() }) }
+ coVerify {
+ timetableDb.removeOldAndSaveNew(
+ oldItems = match { it.isEmpty() },
+ newItems = match { it.isEmpty() },
+ )
+ }
}
private fun createTimetableRemote(
From b319bb03cd722dedd68682d9d7fae04784358bea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sat, 2 Mar 2024 17:31:44 +0100
Subject: [PATCH 21/25] New Crowdin updates (#2458)
---
app/src/main/res/values-cs/strings.xml | 5 +++--
app/src/main/res/values-de/strings.xml | 5 +++--
app/src/main/res/values-pl/strings.xml | 5 +++--
app/src/main/res/values-ru/strings.xml | 5 +++--
app/src/main/res/values-sk/strings.xml | 5 +++--
app/src/main/res/values-uk/strings.xml | 5 +++--
6 files changed, 18 insertions(+), 12 deletions(-)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index fbc92e46..48b43ae4 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -98,8 +98,8 @@
Přihlásit se
Relace vypršela
Relace vypršela. Přihlaste se prosím znovu
- Heslo k vašemu účtu bylo změněno. Musíte se znovu přihlásit do Wulkanového
- Heslo bylo změněno
+ Password has expired or been changed
+ Your account password has expired or been changed. You will need to log in to Wulkanowy again
Podpora aplikace
Líbí se Vám tato aplikace? Podpořte její vývoj tím, že povolíte neinvazivní reklamy, které můžete kdykoliv vypnout
Zapnout reklamy
@@ -860,6 +860,7 @@
This account is inactive. Try logging in again
Nelze se připojit ke deníku. Servery mohou být přetíženy. Prosím zkuste to znovu později
Načítání dat se nezdařilo. Prosím zkuste to znovu později
+ Your password has expired or been changed. Please log in again
Je vyžadována změna hesla pro deník
Probíhá údržba deníku UONET+. Zkuste to později znovu
Neznámá chyba deniku UONET+. Prosím zkuste to znovu později
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 1f124600..ce3ab0d9 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -98,8 +98,8 @@
Anmelden
Die Sitzung ist abgelaufen
Die Sitzung ist abgelaufen, bitte loggen Sie sich erneut ein
- Your account password has been changed. You need to log in to Wulkanowy again
- Password changed
+ Password has expired or been changed
+ Your account password has expired or been changed. You will need to log in to Wulkanowy again
Anwendungsunterstützung
Gefällt Ihnen diese App? Unterstützen Sie ihre Entwicklung, indem Sie nicht-invasive Werbung aktivieren, die Sie jederzeit deaktivieren können
Werbung aktivieren
@@ -766,6 +766,7 @@
This account is inactive. Try logging in again
Registrierungsverbindung fehlgeschlagen. Server können überlastet sein. Bitte versuchen Sie es später noch einmal
Das Laden der Daten ist fehlgeschlagen. Bitte versuchen Sie es später noch einmal
+ Your password has expired or been changed. Please log in again
Passwortänderung für Registrierung erforderlich
Wartung im Gange UONET + Klassenbuch. Versuchen Sie es später noch einmal
Unbekannter UONET + Registerfehler. Versuchen Sie es später erneut
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 597d843d..a193da1b 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -98,8 +98,8 @@
Zaloguj się
Sesja wygasła
Sesja wygasła, zaloguj się ponownie
- Hasło do Twojego konta zostało zmienione. Musisz zalogować się ponownie do Wulkanowego
- Hasło zostało zmienione
+ Hasło wygasło lub zostało zmienione
+ Hasło do twojego konta wygasło lub zostało zmienione. Musisz zalogować się ponownie do Wulkanowego
Wparcie aplikacji
Podoba Ci się ta aplikacja? Wspieraj jej rozwój poprzez włączenie nieinwazyjnych reklam, które możesz wyłączyć w dowolnym momencie
Włącz reklamy
@@ -860,6 +860,7 @@
Konto jest nieaktywne. Spróbuj zalogować się ponownie
Nie udało się połączyć z dziennikiem. Serwery mogą być przeciążone. Spróbuj ponownie później
Ładowanie danych nie powiodło się. Spróbuj ponownie później
+ Twoje hasło wygasło lub zostało zmienione. Zaloguj się ponownie
Wymagana zmiana hasła do dziennika
Trwa przerwa techniczna dziennika UONET+. Spróbuj ponownie później
Nieznany błąd dziennika UONET+. Spróbuj ponownie później
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 46a19c71..590dc13d 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -98,8 +98,8 @@
Войти
Сеанс истёк
Сеанс истёк, авторизуйтесь снова
- Your account password has been changed. You need to log in to Wulkanowy again
- Password changed
+ Password has expired or been changed
+ Your account password has expired or been changed. You will need to log in to Wulkanowy again
Поддержка приложения
Вам нравится это приложение? Поддержите его разработку, включив неинвазивную рекламу, которую можно отключить в любое время
Включить рекламу
@@ -860,6 +860,7 @@
This account is inactive. Try logging in again
Не удалось подключиться к дневнику. Возможно, сервера перегружены, повторите попытку позже
Не удалось загрузить данные, повторите попытку позже
+ Your password has expired or been changed. Please log in again
Необходимо изменить пароль дневника
UONET+ проводит техническое обслуживание, повторите попытку позже
Неизвестная ошибка дневника UONET+, повторите попытку позже
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index b63c07c6..93d7559a 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -98,8 +98,8 @@
Prihlásiť sa
Relácia vypršala
Relácia vypršala. Prihláste sa prosím znovu
- Heslo k vášmu účtu bolo zmenené. Musíte sa znovu prihlásiť do Wulkanového
- Heslo bolo zmenené
+ Password has expired or been changed
+ Your account password has expired or been changed. You will need to log in to Wulkanowy again
Podpora aplikácie
Páči sa Vám táto aplikácia? Podporte jej vývoj tým, že povolíte neinvazívne reklamy, ktoré môžete kedykoľvek vypnúť
Zapnúť reklamy
@@ -860,6 +860,7 @@
This account is inactive. Try logging in again
Nedá sa pripojiť ku denníku. Servery môžu byť preťažené. Prosím skúste to znova neskôr
Načítanie údajov zlyhalo. Skúste neskôr prosím
+ Your password has expired or been changed. Please log in again
Je vyžadovaná zmena hesla pre denník
Prebieha údržba denníka UONET+. Skúste to neskôr znova
Neznáma chyba dennika UONET+. Prosím skúste to znova neskôr
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 8116c7e4..40fc96c1 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -98,8 +98,8 @@
Увійти
Минув термін дії сесії
Минув термін дії сесії, авторизуйтеся знову
- Пароль вашого облікового запису був змінений. Ви повинні увійти в Wulkanowy знову
- Пароль змінено
+ Password has expired or been changed
+ Your account password has expired or been changed. You will need to log in to Wulkanowy again
Підтримка додатку
Вам подобається цей додаток? Підтримайте його розвиток, увімкнувши неінвазивну рекламу, яку ви можете відключити в будь-який час
Увімкнути рекламу
@@ -860,6 +860,7 @@
This account is inactive. Try logging in again
Помилка підключення до щоденнику. Сервери можуть бути перевантажені, спробуйте пізніше
Помилка завантаження даних, спробуйте пізніше
+ Your password has expired or been changed. Please log in again
Необхідна зміна пароля щоденника
UONET+ проводить технічне осблуговування, спробуйте пізніше
Невідома помилка щоденника UONET+, спробуйте пізніше
From 3bab883a5692539e27d6addc45f3630a5b6f5770 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 2 Mar 2024 19:49:08 +0100
Subject: [PATCH 22/25] Add a message explaining the reason for the captcha to
the captcha dialog (#2459)
---
.../io/github/wulkanowy/data/DataModule.kt | 23 +++++++++-------
.../ui/modules/captcha/CaptchaDialog.kt | 5 ++++
.../utils/WebkitCookieManagerProxy.kt | 10 ++++---
app/src/main/res/layout/dialog_captcha.xml | 27 ++++++++++++++++---
app/src/main/res/values/strings.xml | 3 ++-
5 files changed, 51 insertions(+), 17 deletions(-)
diff --git a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt
index 6b6c9d32..50d6c8f9 100644
--- a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt
+++ b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt
@@ -38,17 +38,20 @@ internal class DataModule {
@Singleton
@Provides
- fun provideSdk(chuckerInterceptor: ChuckerInterceptor, remoteConfig: RemoteConfigHelper) =
- Sdk().apply {
- androidVersion = android.os.Build.VERSION.RELEASE
- buildTag = android.os.Build.MODEL
- userAgentTemplate = remoteConfig.userAgentTemplate
- setSimpleHttpLogger { Timber.d(it) }
- setAdditionalCookieManager(WebkitCookieManagerProxy())
+ fun provideSdk(
+ chuckerInterceptor: ChuckerInterceptor,
+ remoteConfig: RemoteConfigHelper,
+ webkitCookieManagerProxy: WebkitCookieManagerProxy,
+ ) = Sdk().apply {
+ androidVersion = android.os.Build.VERSION.RELEASE
+ buildTag = android.os.Build.MODEL
+ userAgentTemplate = remoteConfig.userAgentTemplate
+ setSimpleHttpLogger { Timber.d(it) }
+ setAdditionalCookieManager(webkitCookieManagerProxy)
- // for debug only
- addInterceptor(chuckerInterceptor, network = true)
- }
+ // for debug only
+ addInterceptor(chuckerInterceptor, network = true)
+ }
@Singleton
@Provides
diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/captcha/CaptchaDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/captcha/CaptchaDialog.kt
index ed8293a9..98b4fda7 100644
--- a/app/src/main/java/io/github/wulkanowy/ui/modules/captcha/CaptchaDialog.kt
+++ b/app/src/main/java/io/github/wulkanowy/ui/modules/captcha/CaptchaDialog.kt
@@ -13,6 +13,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.DialogCaptchaBinding
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.base.BaseDialogFragment
+import io.github.wulkanowy.utils.WebkitCookieManagerProxy
import timber.log.Timber
import javax.inject.Inject
@@ -22,6 +23,9 @@ class CaptchaDialog : BaseDialogFragment() {
@Inject
lateinit var sdk: Sdk
+ @Inject
+ lateinit var webkitCookieManagerProxy: WebkitCookieManagerProxy
+
private var webView: WebView? = null
companion object {
@@ -80,6 +84,7 @@ class CaptchaDialog : BaseDialogFragment() {
}
override fun onDestroy() {
+ webkitCookieManagerProxy.webkitCookieManager?.flush()
webView?.destroy()
super.onDestroy()
}
diff --git a/app/src/main/java/io/github/wulkanowy/utils/WebkitCookieManagerProxy.kt b/app/src/main/java/io/github/wulkanowy/utils/WebkitCookieManagerProxy.kt
index 3d41c711..4d2dde78 100644
--- a/app/src/main/java/io/github/wulkanowy/utils/WebkitCookieManagerProxy.kt
+++ b/app/src/main/java/io/github/wulkanowy/utils/WebkitCookieManagerProxy.kt
@@ -5,17 +5,21 @@ import java.net.CookiePolicy
import java.net.CookieStore
import java.net.HttpCookie
import java.net.URI
+import javax.inject.Inject
+import javax.inject.Singleton
import android.webkit.CookieManager as WebkitCookieManager
import java.net.CookieManager as JavaCookieManager
-class WebkitCookieManagerProxy : JavaCookieManager(null, CookiePolicy.ACCEPT_ALL) {
+@Singleton
+class WebkitCookieManagerProxy @Inject constructor() :
+ JavaCookieManager(null, CookiePolicy.ACCEPT_ALL) {
- private val webkitCookieManager: WebkitCookieManager? = getWebkitCookieManager()
+ val webkitCookieManager: WebkitCookieManager? = getCookieManager()
/**
* @see [https://stackoverflow.com/a/70354583/6695449]
*/
- private fun getWebkitCookieManager(): WebkitCookieManager? {
+ private fun getCookieManager(): WebkitCookieManager? {
return try {
WebkitCookieManager.getInstance()
} catch (e: AndroidRuntimeException) {
diff --git a/app/src/main/res/layout/dialog_captcha.xml b/app/src/main/res/layout/dialog_captcha.xml
index 539aa0cc..019d8932 100644
--- a/app/src/main/res/layout/dialog_captcha.xml
+++ b/app/src/main/res/layout/dialog_captcha.xml
@@ -7,15 +7,18 @@
tools:context=".ui.modules.captcha.CaptchaDialog">
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="0" />
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/captcha_webview" />
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 266c3522..2775365d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -848,7 +848,8 @@
- Verification is in progress. Wait…
+ VULCAN\'s website requires verification
+ Why am I seeing this?\nThe register website from which Wulkanowy downloads data displays the same screen as above, so Wulkanowy must also show it to be able to download data from this website. There\'s no way around it
Verified successfully
From a0a0b8dea6e70b32e310158caeee317026326bb4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Borcz?=
Date: Sat, 2 Mar 2024 20:37:36 +0100
Subject: [PATCH 23/25] New Crowdin updates (#2460)
---
app/src/main/res/values-cs/strings.xml | 27 +++++++++++++-------------
app/src/main/res/values-de/strings.xml | 3 ++-
app/src/main/res/values-pl/strings.xml | 3 ++-
app/src/main/res/values-ru/strings.xml | 3 ++-
app/src/main/res/values-sk/strings.xml | 27 +++++++++++++-------------
app/src/main/res/values-uk/strings.xml | 27 +++++++++++++-------------
6 files changed, 48 insertions(+), 42 deletions(-)
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 48b43ae4..c3c691c7 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -56,7 +56,7 @@
Neplatný e-mail
Místo e-mailu použijte přiřazené přihlašovací údaje
Použijte přiřazené přihlašovací nebo e-mail v @%1$s
- Invalid domain suffix
+ Neplatná přípona domény
Neplatný symbol. Pokud jej nemůžete najít, kontaktujte školu
Nevymýšlejte si! Pokud symbol nemůžete najít, kontaktujte školu
Žák nebyl nalezen. Zkontrolujte správnost symbolu a vybrané varianty deníku UONET+
@@ -98,8 +98,8 @@
Přihlásit se
Relace vypršela
Relace vypršela. Přihlaste se prosím znovu
- Password has expired or been changed
- Your account password has expired or been changed. You will need to log in to Wulkanowy again
+ Heslo vypršelo nebo bylo změněno
+ Platnost hesla k vašemu účtu vypršela nebo bylo změněno. Budete se muset znovu přihlásit do Wulkanového
Podpora aplikace
Líbí se Vám tato aplikace? Podpořte její vývoj tím, že povolíte neinvazivní reklamy, které můžete kdykoliv vypnout
Zapnout reklamy
@@ -336,10 +336,10 @@
Poslat dále
Vybrat vše
Odznačit vše
- Restore from trash
+ Obnovit z koše
Přesunout do koše
Odstranit natrvalo
- Message restored successfully
+ Zpráva úspěšně obnovena
Zpráva byla úspěšně odstraněna
žák
rodič
@@ -385,7 +385,7 @@
- %1$d vybraných
Zprávy odstraněné
- Messages restored
+ Obnovené zprávy
Vyberte poštovní schránku
Anonymní režim je zapnutý
Díky anonymnímu režimu není odesílatel upozorněn, když si zprávu přečtete
@@ -852,15 +852,16 @@
Pro provoz aplikace potřebujeme potvrdit vaši identitu. Zadejte PESEL žáka <b>%1$s</b> v níže uvedeném poli
Zatím přeskočit
- Probíhá ověřování. Počkejte…
+ VULCAN\'s website requires verification
+ Why am I seeing this?\nThe register website from which Wulkanowy downloads data displays the same screen as above, so Wulkanowy must also show it to be able to download data from this website. There\'s no way around it
Úspěšně ověřeno
Žádné internetové připojení
Vyskytla se chyba. Zkontrolujte hodiny svého zařízení
- This account is inactive. Try logging in again
+ Tento účet je neaktivní. Zkuste se znovu přihlásit
Nelze se připojit ke deníku. Servery mohou být přetíženy. Prosím zkuste to znovu později
Načítání dat se nezdařilo. Prosím zkuste to znovu později
- Your password has expired or been changed. Please log in again
+ Vaše heslo vypršelo nebo bylo změněno. Přihlaste se znovu
Je vyžadována změna hesla pro deník
Probíhá údržba deníku UONET+. Zkuste to později znovu
Neznámá chyba deniku UONET+. Prosím zkuste to znovu později
@@ -871,8 +872,8 @@
Funkce není k dispozici. Přihlaste se v jiném režimu než Mobile API
Toto pole je povinné
- Mute
- Unmute
- You have muted this user
- You have unmuted this user
+ Ztlumit
+ Zrušit ztlumení
+ Ztlumili jste tohoto uživatele
+ Zrušili jste ztlumení tohoto uživatele
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index ce3ab0d9..daabc7d8 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -758,7 +758,8 @@
To operate the application, we need to confirm your identity. Please enter the student\'s PESEL <b>%1$s</b> in the field below
Skip for now
- Verification is in progress. Wait…
+ VULCAN\'s website requires verification
+ Why am I seeing this?\nThe register website from which Wulkanowy downloads data displays the same screen as above, so Wulkanowy must also show it to be able to download data from this website. There\'s no way around it
Verified successfully
Keine Internetverbindung
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index a193da1b..33b715d7 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -852,7 +852,8 @@
Rodzicu, musimy mieć pewność, że Twój adres e-mail został powiązany z prawidłowym kontem ucznia. W celu autoryzacji konta podaj numer PESEL ucznia <b>%1$s</b> w polu poniżej
Na razie pomiń
- Trwa weryfikacja. Czekaj…
+ Strona dziennika VULCAN wymaga weryfikacji
+ Dlaczego to widzę?\nStrona internetowa dziennika, z której Wulkanowy pobiera dane, wyświetla ten sam ekran jak powyżej, więc Wulkanowy musi również ją pokazać, aby móc pobrać dane z tej witryny. Nie da się tego obejść
Pomyślnie zweryfikowano
Brak połączenia z internetem
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 590dc13d..8a5fcc40 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -852,7 +852,8 @@
Для работы приложения нам необходимо подтвердить вашу личность. Введите PESEL учащегося <b>%1$s</b> в поле ниже
Пропустить сейчас
- Verification is in progress. Wait…
+ VULCAN\'s website requires verification
+ Why am I seeing this?\nThe register website from which Wulkanowy downloads data displays the same screen as above, so Wulkanowy must also show it to be able to download data from this website. There\'s no way around it
Verified successfully
Интернет-соединение отсутствует
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 93d7559a..829475d6 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -56,7 +56,7 @@
Neplatný e-mail
Namiesto e-mailu použite priradené prihlasovacie údaje
Použite priradené prihlasovacie alebo e-mail v @%1$s
- Invalid domain suffix
+ Neplatná prípona domény
Neplatný symbol. Pokiaľ ho nemôžete nájsť, kontaktujte školu
Nevymýšľajte si! Pokiaľ symbol nemôžete nájsť, kontaktujte školu
Žiak nebol nájdený. Skontrolujte správnosť symbolu a vybrané varianty denníka UONET+
@@ -98,8 +98,8 @@
Prihlásiť sa
Relácia vypršala
Relácia vypršala. Prihláste sa prosím znovu
- Password has expired or been changed
- Your account password has expired or been changed. You will need to log in to Wulkanowy again
+ Heslo vypršalo alebo bolo zmenené
+ Platnosť hesla k vášmu účtu vypršala alebo bolo zmenené. Budete sa musieť znova prihlásiť do Wulkanového
Podpora aplikácie
Páči sa Vám táto aplikácia? Podporte jej vývoj tým, že povolíte neinvazívne reklamy, ktoré môžete kedykoľvek vypnúť
Zapnúť reklamy
@@ -336,10 +336,10 @@
Poslať ďalej
Vybrať všetko
Odznačiť všetko
- Restore from trash
+ Obnoviť z koša
Presunúť do koša
Odstrániť natrvalo
- Message restored successfully
+ Správa úspešne obnovená
Správa bola úspešne odstránená
žiak
rodič
@@ -385,7 +385,7 @@
- %1$d vybraných
Správy odstránené
- Messages restored
+ Obnovené správy
Vyberte poštovú schránku
Režim inkognito je zapnutý
Vďaka inkognito režimu nie je odosielateľ upozornený, keď si správu prečítate
@@ -852,15 +852,16 @@
Na prevádzku aplikácie potrebujeme potvrdiť vašu identitu. Zadajte PESEL žiaka <b>%1$s</b> v nižšie uvedenom poli
Zatiaľ preskočiť
- Overovanie prebieha. Počkajte…
+ VULCAN\'s website requires verification
+ Why am I seeing this?\nThe register website from which Wulkanowy downloads data displays the same screen as above, so Wulkanowy must also show it to be able to download data from this website. There\'s no way around it
Úspešne overené
Žiadne internetové pripojenie
Vyskytla sa chyba. Skontrolujte hodiny svojho zariadenia
- This account is inactive. Try logging in again
+ Tento účet je neaktívny. Skúste sa znova prihlásiť
Nedá sa pripojiť ku denníku. Servery môžu byť preťažené. Prosím skúste to znova neskôr
Načítanie údajov zlyhalo. Skúste neskôr prosím
- Your password has expired or been changed. Please log in again
+ Vaše heslo vypršalo alebo bolo zmenené. Prihláste sa znova
Je vyžadovaná zmena hesla pre denník
Prebieha údržba denníka UONET+. Skúste to neskôr znova
Neznáma chyba dennika UONET+. Prosím skúste to znova neskôr
@@ -871,8 +872,8 @@
Funkcia nie je k dispozícii. Prihláste sa v inom režime než Mobile API
Toto pole je povinné
- Mute
- Unmute
- You have muted this user
- You have unmuted this user
+ Stlmiť
+ Zrušiť stlmenie
+ Stlmili ste tohto používateľa
+ Zrušili ste stlmenie tohto používateľa
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 40fc96c1..a0d4b6c0 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -56,7 +56,7 @@
Недійсна адреса e-mail
Використовуйте призначений логін замість адреси e-mail
Використовуйте призначений логін або адресу e-mail в @%1$s
- Invalid domain suffix
+ Невірний суфікс домену
Некоректний символ. Якщо ви не можете знайти його, будь ласка, зв\'яжіться зі школою
Не вигадуйте! Якщо ви не можете знайти його, будь ласка, зв\'яжіться зі школою
Студента не знайдено. Перевірте symbol та обраний тип щоденника UONET+
@@ -98,8 +98,8 @@
Увійти
Минув термін дії сесії
Минув термін дії сесії, авторизуйтеся знову
- Password has expired or been changed
- Your account password has expired or been changed. You will need to log in to Wulkanowy again
+ Термін дії пароля закінчився або його було змінено
+ Термін дії пароля для вашого облікового запису закінчився або було змінено. Необхідно зайти в Wulkanowy знову
Підтримка додатку
Вам подобається цей додаток? Підтримайте його розвиток, увімкнувши неінвазивну рекламу, яку ви можете відключити в будь-який час
Увімкнути рекламу
@@ -336,10 +336,10 @@
Переслати
Вибрати всі
Відмінити вибір
- Restore from trash
+ Відновити зі смітника
Перемістити до кошика
Видалити назавжди
- Message restored successfully
+ Повідомлення успішно відновлено
Лист було успішно видалено
учень
родич
@@ -385,7 +385,7 @@
- %1$d вибрано
Листи видалено
- Messages restored
+ Повідомлення відновлені
Вибрати поштову скриньку
Режим анонімності включено
Завдяки режиму анонімності, відправник не буде сповіщений коли ви прочитаєте повідомлення
@@ -852,15 +852,16 @@
Для роботи програми нам потрібно підтвердити вашу особу. Будь ласка, введіть число PESEL <b>%1$s</b> студента в поле нижче
Поки що пропустити
- Верифікація в процесі. Чекайте…
+ Веб-сайт VULCAN потребує підтвердження
+ Чому я це бачу?\nСайт реєстру, з якого Wulkanowy завантажує дані, відображає той самий екран, що й вище, тому Wulkanowy також повинен показувати його, щоб мати змогу завантажувати дані з цього сайту. Це неможливо обійти
Верифікація завершена
Немає з\'єднання з інтернетом
Сталася помилка. Перевірте годинник пристрою
- This account is inactive. Try logging in again
+ Цей обліковий запис неактивний. Спробуйте увійти ще раз
Помилка підключення до щоденнику. Сервери можуть бути перевантажені, спробуйте пізніше
Помилка завантаження даних, спробуйте пізніше
- Your password has expired or been changed. Please log in again
+ Термін дії вашого пароля минув або був змінений. Будь ласка увійдіть знову
Необхідна зміна пароля щоденника
UONET+ проводить технічне осблуговування, спробуйте пізніше
Невідома помилка щоденника UONET+, спробуйте пізніше
@@ -871,8 +872,8 @@
Функція недоступна в режимі Mobile API. Увійдіть в інший режим
Це поле обовʼязкове
- Mute
- Unmute
- You have muted this user
- You have unmuted this user
+ Вимкнути сповіщення
+ Ввімкнути сповіщення
+ Ви ігноруєте цього користувача
+ Ви не ігноруєте цього користувача
From 2bbc157d0361b0f23c1d37b174e7e60f0e3b3c07 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 2 Mar 2024 20:45:23 +0100
Subject: [PATCH 24/25] Add some new symbols to symbol autocomplete field
(#2461)
---
app/src/main/res/values/api_symbols.xml | 276 ++++++++++++++++++++++++
1 file changed, 276 insertions(+)
diff --git a/app/src/main/res/values/api_symbols.xml b/app/src/main/res/values/api_symbols.xml
index 4b61db48..510995b9 100644
--- a/app/src/main/res/values/api_symbols.xml
+++ b/app/src/main/res/values/api_symbols.xml
@@ -1,6 +1,8 @@
+ - Adamów, powiat łukowski
+ - Aleksandrów, powiat biłgorajski
- Andrychów
- Augustów
- Baranów Sandomierski
@@ -8,6 +10,8 @@
- Bełchatów
- Bełżyce
- Biała Podlaska
+ - Biała, powiat prudnicki
+ - Biała, powiat wielunski
- Biała Rawska
- Biały Bór
- Białystok
@@ -23,11 +27,17 @@
- Boguchwała
- Boguty-Pianki
- Bolesławiec
+ - Bolesław, powiat dąbrowski
- Braniewo
- Brodnica
+ - Brodnica, powiat śremski
+ - Brody, powiat starachowicki
+ - Brójce, powiat łódzki wschodni
- Brwinów
- Brzeg
- Brzeski
+ - Powiat brzeski
+ - Brzeźnica, powiat wadowicki
- Buk
- Bukowno
- Busko-Zdrój
@@ -36,6 +46,7 @@
- Bystrzyca Kłodzka
- Bytom
- Bytom Odrzański
+ - CECH bialski
- Chełm
- Chełmno
- Chełmża
@@ -44,18 +55,28 @@
- Chojnice
- Chojnów
- Chorzów
+ - Chrzanów, powiat janowski
- Ciechanów
- Cieszyn
+ - Czarna, powiat bieszczadzki
+ - Czarna, powiat dębicki
- Czarnków
- Czeladź
+ - Czermin, powiat mielecki
+ - Czermin, powiat pleszewski
- Czersk
- Częstochowa
- Człuchów
+ - Dąbie, powiat krośnieński
- Dąbrowa Białostocka
- Dąbrowa Górnicza
+ - Dabrowa, powiat opolski
- Dąbrowa Tarnowska
- Dębica
- Dębno
+ - Dębowiec, powiat jasielski
+ - Dobra, powiat lobeski
+ - Dobre, powiat radziejowski
- Dobrzeń Wielki
- Dobrzeń Wielki 2
- Dobrzyń Nad Wisłą
@@ -67,6 +88,8 @@
- Elbląg
- Ełk
- Frampol
+ - Fundacja Elementarz
+ - Fundacja Mozaika
- Garwolin
- Gdańsk
- Gdynia
@@ -124,6 +147,7 @@
- Gmina Brańszczyk
- Gmina Brąszewice
- Gmina Brenna
+ - Gmina Brochów
- Gmina Brok
- Gmina Brzeg Dolny
- Gmina Brzeziny
@@ -221,6 +245,7 @@
- Gmina Działoszyce
- Gmina Dziemiany
- Gmina Dzierżoniów
+ - Gmina Dziwnów
- Gmina Dzwola
- Gmina Elbląg
- Gmina Ełk
@@ -296,6 +321,7 @@
- Gmina Hrubieszów
- Gmina Huszlew
- Gmina Hyżne
+ - Gmina Igołomia-Wawrzeńczyce
- Gmina Imielno
- Gmina Inowrocław
- Gmina Irządze
@@ -339,6 +365,7 @@
- Gmina Kamienica
- Gmina Kamiennik
- Gmina Kamionka
+ - Gmina Kampinos
- Gmina Karczmiska
- Gmina Kargowa
- Gmina Karlino
@@ -410,6 +437,7 @@
- Gmina Krasocin
- Gmina Krempna
- Gmina Krokowa
+ - Gmina Krościenko
- Gmina Krośnice
- Gmina Krupski Młyn
- Gmina Kruszwica
@@ -477,6 +505,7 @@
- Gmina Łopiennik Górny
- Gmina Łopuszno
- Gmina Łosice
+ - Gmina Łososina Dolna
- Gmina Lubań
- Gmina Lubartów
- Gmina Lubasz
@@ -524,6 +553,7 @@
- Gmina Miejsce Piastowe
- Gmina Miękinia
- Gmina Mielec
+ - Gmina Mieleszyn
- Gmina Mielno
- Gmina Mieszkowice
- Gmina Milanów
@@ -575,6 +605,7 @@
- Gmina Nowy Kawęczyn
- Gmina Nowy Korczyn
- Gmina Nowy Staw
+ - Gmina Nowy Wiśnicz
- Gmina Nowy Targ
- Gmina Nowy Tomyśl
- Gmina Nozdrzec
@@ -587,6 +618,7 @@
- Gmina Olszyna
- Gmina Opatowiec
- Gmina Orneta
+ - Gmina Orchowo
- Gmina Osieczna
- Gmina Osiek
- Gmina Osiek Jasielski
@@ -629,9 +661,12 @@
- Gmina Piątnica
- Gmina Piekoszów
- Gmina Pieniężno
+ - Gmina Pietrowice Wielkie
- Gmina Pilchowice
+ - Gmina Pielgrzymka
- Gmina Pińczów
- Gmina Pionki
+ - Gmina Piszczac
- Gmina Płaska
- Gmina Platerówka
- Gmina Pleśna
@@ -655,6 +690,7 @@
- Gmina Popów
- Gmina Potęgowo
- Gmina Potok Wielki
+ - Gmina Paradyż
- Gmina Praszka
- Gmina Prochowice
- Gmina Promna
@@ -733,6 +769,7 @@
- Gmina Sanok
- Gmina Sawin
- Gmina Ścinawa
+ - Gmina Secemin
- Gmina Sędziejowice
- Gmina Sejny
- Gmina Sękowa
@@ -758,6 +795,7 @@
- Gmina Sitno
- Gmina Skarżysko Kościelne
- Gmina Skępe
+ - Gmina Skierbieszów
- Gmina Skierniewice
- Gmina Skoczów
- Gmina Skoki
@@ -779,6 +817,7 @@
- Gmina Sobótka
- Gmina Sokółka
- Gmina Solina
+ - Gmina Somonino
- Gmina Sośnicowice
- Gmina Sośnie
- Gmina Sośno
@@ -800,11 +839,13 @@
- Gmina Stoczek Łukowski
- Gmina Stopnica
- Gmina Strawczyn
+ - Gmina Stromiec
- Gmina Stryków
- Gmina Stryszawa
- Gmina Stryszów
- Gmina Strzałkowo
- Gmina Strzelce Opolskie
+ - Gmina Strzelce Opolskie 2
- Gmina Strzelin
- Gmina Strzelno
- Gmina Strzyżewice
@@ -846,6 +887,7 @@
- Gmina Tarnów
- Gmina Tarnowiec
- Gmina Tarnów Opolski
+ - Gmina Tarnów Opolski 2
- Gmina Teresin
- Gmina Tereszpol
- Gmina Tłuchowo
@@ -870,6 +912,7 @@
- Gmina Tyrawa Wołoska
- Gmina Uchanie
- Gmina Ujazd
+ - Gmina PSP Ujazd
- Gmina Ulan-Majorat
- Gmina Ulanów
- Gmina Ułęż
@@ -898,6 +941,7 @@
- Gmina Wielgomłyny
- Gmina Wieliszew
- Gmina Wielka Nieszawka
+ - Gmina Gmina Wielopole Skrzyńskie
- Gmina Wieniawa
- Gmina Wieprz
- Gmina Wieruszów
@@ -937,6 +981,7 @@
- Gmina Wolsztyn
- Gmina Wręczyca Wielka
- Gmina Wronki
+ - Gmina Wyryki
- Gmina Wyrzysk
- Gmina Wysokie
- Gmina Żabno
@@ -980,6 +1025,7 @@
- Gmina Żołynia
- Gmina Żukowice
- Gmina Żurawica
+ - Gmina Żychlin
- Gmina Żyraków
- Gmina Żyrzyn
- Gmina Żytno
@@ -992,9 +1038,11 @@
- Górzno
- Gorzów Śląski
- Gorzów Wielkopolski
+ - Gorzyce powiat tarnobrzeski
- Gostynin
- Grajewo
- Grodzisk Mazowiecki
+ - Grodzisk Wielkopolski
- Grudziądz
- Grybów
- Gryfino
@@ -1002,10 +1050,14 @@
- Hel
- Hrubieszów
- Inowrocław
+ - IR Tarnów
- Izbica Kujawska
+ - Jabłonna, powiat legionowski
+ - Jabłonna, powiat lubelski
- Jabłonowo Pomorskie
- Janowiec Wielkopolski
- Janów Lubelski
+ - Janów, powiat sokolski
- Jarocin
- Jarosław
- Jasło
@@ -1040,6 +1092,7 @@
- Kołobrzeg
- Koniecpol
- Konin
+ - Konopnica powiat lubelski
- Konstancin-Jeziorna
- Konstantynów Łódzki
- Koronowo
@@ -1060,6 +1113,7 @@
- Krosno
- Krotoszyce
- Krotoszyn
+ - Krynica
- Krzeszowice
- Krzyż Wielkopolski
- Książ Wielkopolski
@@ -1079,12 +1133,14 @@
- Lędziny
- Legionowo
- Legnica
+ - Leśnica opolska
- Leszno
- Lewin Brzeski
- Lewin Brzeski 2
- Leżajsk
- Limanowa
- Lipno
+ - Lipno, powiat lipnowski
- Łódź
- Łódzkie
- Łowicz
@@ -1095,6 +1151,7 @@
- Lubin
- Lublin
- Lubliniec
+ - Lubnice, powiat staszowski
- Lubuskie
- Łuków
- Lwówecki
@@ -1102,18 +1159,30 @@
- Malbork
- Małopolskie
- Marki
+ - Maszewo, powiat goleniowski
- Mazowieckie
+ - MEN
+ - Miasto Toruń
- Michałowice
- Miechów
- Międzyrzec Podlaski
- Miejska Górka
- Mielec
- Milanówek
+ - Ministerstwo Rolnictwa
- Mińsk Mazowiecki
+ - Ministerstwo Kultury i Dziedzictwa Narodowego
- Mniszków
+ - Ministerstwo Nauki i Szkolnictwa
+ - Ministerstwo Obrony Narodowej
+ - Ministerstwo Środowiska
- Mosina
+ - Moszczenica powiat gorlicki
+ - Moszczenica powiat piotrkowski
- Mrągowo
- Mrągowski
+ - Ministerstwo Sprawiedliwości
+ - Ministerstwo Spraw Wewnętrznych
- Mszana Dolna
- Mszczonów
- Muszyna
@@ -1137,9 +1206,17 @@
- Nowy Żmigród
- Nysa
- Oborniki Śląskie
+ - Oborniki Wielkopolskie
- Obrzycko
+ - Oleśnica, powiat olesnicki
+ - Oleśnica, powiat staszowski
+ - Olesno powiat dąbrowski
+ - Olesno powiat oleski
- Olkusz
+ - Olszanka, powiat brzeski
- Olsztyn
+ - Opatów, powiat kłobucki
+ - Opatów, powiat opatowski
- Opinogóra Górna
- Opoczno
- Opole
@@ -1148,8 +1225,10 @@
- Orzesze
- Osieczna
- Osiecznica
+ - Osiek, powiat starogardzki
- Ostróda
- Ostrołęka
+ - Ostrowiec Świętokrzyski
- Ostrów Wielkopolski
- Oświęcim
- Otwock
@@ -1166,6 +1245,7 @@
- Pilzno
- Piotrków Trybunalski
- Pisz
+ - Piwniczna
- Płock
- Płońsk
- Pniewy
@@ -1177,6 +1257,8 @@
- Pomorskie
- Poniec
- Poręba
+ - Poświętne, powiat opoczyński
+ - Poświętne, powiat wołomiński
- Powiat aleksandrowski
- Powiat augustowski
- Powiat będziński
@@ -1217,6 +1299,7 @@
- Powiat giżycki
- Powiat gliwicki
- Powiat głogowski
+ - Powiat głubczycki
- Powiat gnieźnieński
- Powiat gołdapski
- Powiat goleniowski
@@ -1226,6 +1309,8 @@
- Powiat gorzowski
- Powiat gostyński
- Powiat grajewski
+ - Powiat grodziski, mazowieckie
+ - Powiat grodziski, wielkopolskie
- Powiat grójecki
- Powiat gryficki
- Powiat gryfiński
@@ -1293,6 +1378,7 @@
- Powiat makowski
- Powiat malborski
- Powiat miechowski
+ - Powiat międzyrzecki
- Powiat mielecki
- Powiat mikołowski
- Powiat milicki
@@ -1321,17 +1407,21 @@
- Powiat olsztyński
- Powiat opatowski
- Powiat opoczyński
+ - Powiat opole lubelskie
- Powiat opolski
+ - Powiat opolski 2
- Powiat ostródzki
- Powiat ostrowiecki
- Powiat ostrzeszowski
- Powiat oświęcimski
+ - Powiat ostrowski, mazowieckie
- Powiat otwocki
- Powiat pabianicki
- Powiat piaseczyński
- Powiat pilski
- Powiat pińczowski
- Powiat piotrkowski
+ - Powiat piski, warmińsko-mazurskie
- Powiat pleszewski
- Powiat płocki
- Powiat płoński
@@ -1391,6 +1481,7 @@
- Powiat suski
- Powiat świdnicki
- Powiat świdwiński
+ - Powiat świdnicki w Świdniku
- Powiat świebodziński
- Powiat świecki
- Powiat szamotulski
@@ -1403,6 +1494,7 @@
- Powiat tatrzański
- Powiat tczewski
- Powiat tomaszowski
+ - Powiat tomaszowski, lubelskie
- Powiat toruński
- Powiat trzebnicki
- Powiat tucholski
@@ -1444,6 +1536,7 @@
- Powiat żyrardowski
- Powiat żywiecki
- Poznań
+ - prfrawamaz
- Proszowice
- Prudnik
- Pruszcz Gdański
@@ -1461,6 +1554,7 @@
- Rabka-Zdrój
- Raciąż
- Racibórz
+ - Radków Kłodzki
- Radom
- Radomsko
- Radomyśl Wielki
@@ -1468,12 +1562,17 @@
- Radziejów
- Radzionków
- Radzyń Podlaski
+ - Rakoniewice
- Rawa Mazowiecka
- Rawicz
- Reda
+ - Rejowiec, powiat chełmski
+ - Rogowo, powiat rypiński
+ - Rogowo, powiat żniński
- Rogóźno
- Ropczyce
- Ruda Śląska
+ - Rudnik, powiat raciborski
- Rumia
- Rybnik
- Rychwał
@@ -1482,6 +1581,7 @@
- Rypin
- Rzeszów
- Rzeszów projekt
+ - Rzgów, powiat koniński
- Sandomierz
- Sanok
- Sędziszów Małopolski
@@ -1503,14 +1603,19 @@
- Sokołów Podlaski
- Sopot
- Sosnowiec
+ - spmajkowskarzysko
+ - spteodory
- Śrem
- Środa Śląska
- Środa Wielkopolska
- Starachowice
- Stargard
- Starogard Gdański
+ - starostwokrosnienskie
- Stary Sącz
- Staszów
+ - stezycapowiatrycki
+ - stowarzyszenieintegracja
- Stronie Śląskie
- Strzyżów
- Sulejówek
@@ -1518,9 +1623,12 @@
- Sulmierzyce
- Swarzędz
- Świdnica
+ - swidnicapowiatswidnicki
+ - swidnicapowiatzielonogorski
- Świdnik
- Świdwin
- Świeradów-Zdrój
+ - swietajnopowiatszczycienski
- Świętochłowice
- Świnoujście
- Syców
@@ -1532,6 +1640,7 @@
- Szprotawa
- Sztum
- Szubin
+ - szydlowopowiatpilski
- Tarnobrzeg
- Tarnów
- Tarnowskie Góry
@@ -1548,6 +1657,7 @@
- Turawa
- Tuszyn
- Tychy
+ - UG Gołcza
- Ujazd
- Ustka
- Ustroń
@@ -1557,6 +1667,20 @@
- Wałcz
- Warmińsko-Mazurskie
- Warszawa
+ - Warszawa Bemowo
+ - Warszawa Białołęka
+ - Warszawa Bielany
+ - Warszawa Mokotów
+ - Warszawa Praga Południe
+ - Warszawa Śródmiśscie
+ - Warszawa Targówek
+ - Warszawa Ursus
+ - Warszawa Ursynow
+ - Warszawa Wawer
+ - Warszawa Wesoła
+ - Warszawa Włochy
+ - Warszawa Wola
+ - Warszawa Żoliborz
- Wąsosz
- Węgrów
- Wejherowo
@@ -1564,6 +1688,10 @@
- Wieliczka
- Wielkopolskie
- Wieluń
+ - Wierzbica, powiat chełmski
+ - Wierzbica, powiat radomski
+ - Wilków, powiat namysłowski
+ - Wiśniowa, powiat myślenicki
- Władysławowo
- Włocławek
- Włodawa
@@ -1580,24 +1708,36 @@
- Żagań
- Zakliczyn
- Zakopane
+ - Zakrzewo, powiat aleksandrowski
- Zambrów
- Zamość
- Żary
- Zawidów
- Zduńska Wola
- Zduny
+ - ZDZ Warszawa
- Żelechów
+ - Zespół Szkół PPC Kumarszew
- Zgierz
- Zgorzelec
- Zielona Góra
- Zielonka
+ - ZKSO 1 Katowice
- Złotoryja
- Złotów
- Żory
+ - ZS2 Lubin
+ - ZSK Sieradz
+ - ZSKZ Kwidzyn
+ - ZSKZ Sochaczew
+ - ZSP Stare Koźle
+ - ZST powiat opoczyński
- Zwoleń
- Żyrardów
+ - adamowpowiatlukowski
+ - aleksandrowpowiatbilgorajski
- andrychow
- augustow
- baranowsandomierski
@@ -1605,6 +1745,8 @@
- belchatow
- belzyce
- bialapodlaska
+ - bialapowiatprudnicki
+ - bialapowiatwielunski
- bialarawska
- bialybor
- bialystok
@@ -1620,11 +1762,17 @@
- boguchwala
- bogutypianki
- boleslawiec
+ - boleslawpowiatdabrowski
- braniewo
- brodnica
+ - brodnicapowiatsremski
+ - brodypowiatstarachowicki
+ - brojcepowiatlodzkiwsch
- brwinow
- brzeg
- brzeski
+ - brzeskipowiat
+ - brzeznicapowiatwadowicki
- buk
- bukowno
- buskozdroj
@@ -1633,6 +1781,7 @@
- bystrzycaklodzka
- bytom
- bytomodrzanski
+ - cechbialski
- chelm
- chelmno
- chelmza
@@ -1641,18 +1790,28 @@
- chojnice
- chojnow
- chorzow
+ - chrzanowpowiatjanowski
- ciechanow
- cieszyn
+ - czarnapowiatbieszczadzki
+ - czarnapowiatdebicki
- czarnkow
- czeladz
+ - czerminpowiatmielecki
+ - czerminpowiatpleszewski
- czersk
- czestochowa
- czluchow
+ - dabiepowiatkrosnienski
- dabrowabialostocka
- dabrowagornicza
+ - dabrowapowiatopolski
- dabrowatarnowska
- debica
- debno
+ - debowiecpowiatjasielski
+ - dobrapowiatlobeski
+ - dobrepowiatradziejowski
- dobrzenwielki
- dobrzenwielki2
- dobrzynnadwisla
@@ -1664,6 +1823,8 @@
- elblag
- elk
- frampol
+ - fundacjaelementarz
+ - fundacjamozaika
- garwolin
- gdansk
- gdynia
@@ -1721,6 +1882,7 @@
- gminabranszczyk
- gminabraszewice
- gminabrenna
+ - gminabrochow
- gminabrok
- gminabrzegdolny
- gminabrzeziny
@@ -1818,6 +1980,7 @@
- gminadzialoszyce
- gminadziemiany
- gminadzierzoniow
+ - gminadziwnow
- gminadzwola
- gminaelblag
- gminaelk
@@ -1893,6 +2056,7 @@
- gminahrubieszow
- gminahuszlew
- gminahyzne
+ - gminaiglomniawawrzenczyce
- gminaimielno
- gminainowroclaw
- gminairzadze
@@ -1936,6 +2100,7 @@
- gminakamienica
- gminakamiennik
- gminakamionka
+ - gminakampinos
- gminakarczmiska
- gminakargowa
- gminakarlino
@@ -2007,6 +2172,7 @@
- gminakrasocin
- gminakrempna
- gminakrokowa
+ - gminakroscienko
- gminakrosnice
- gminakrupskimlyn
- gminakruszwica
@@ -2074,6 +2240,7 @@
- gminalopiennikgorny
- gminalopuszno
- gminalosice
+ - gminalososinadolna
- gminaluban
- gminalubartow
- gminalubasz
@@ -2121,6 +2288,7 @@
- gminamiejscepiastowe
- gminamiekinia
- gminamielec
+ - gminamieleszyn
- gminamielno
- gminamieszkowice
- gminamilanow
@@ -2172,6 +2340,7 @@
- gminanowykaweczyn
- gminanowykorczyn
- gminanowystaw
+ - gminanowyswisnicz
- gminanowytarg
- gminanowytomysl
- gminanozdrzec
@@ -2184,6 +2353,7 @@
- gminaolszyna
- gminaopatowiec
- gminaorneta
+ - gminaorochowo
- gminaosieczna
- gminaosiek
- gminaosiekjasielski
@@ -2226,9 +2396,12 @@
- gminapiatnica
- gminapiekoszow
- gminapieniezno
+ - gminapietrowicewlk
- gminapilchowice
+ - gminapilelgrzymka
- gminapinczow
- gminapionki
+ - gminapiszac
- gminaplaska
- gminaplaterowka
- gminaplesna
@@ -2252,6 +2425,7 @@
- gminapopow
- gminapotegowo
- gminapotokwielki
+ - gminapradyz
- gminapraszka
- gminaprochowice
- gminapromna
@@ -2330,6 +2504,7 @@
- gminasanok
- gminasawin
- gminascinawa
+ - gminasecemin
- gminasedziejowice
- gminasejny
- gminasekowa
@@ -2355,6 +2530,7 @@
- gminasitno
- gminaskarzyskokoscielne
- gminaskepe
+ - gminaskierbieszow
- gminaskierniewice
- gminaskoczow
- gminaskoki
@@ -2376,6 +2552,7 @@
- gminasobotka
- gminasokolka
- gminasolina
+ - gminasomonino
- gminasosnicowice
- gminasosnie
- gminasosno
@@ -2397,11 +2574,13 @@
- gminastoczeklukowski
- gminastopnica
- gminastrawczyn
+ - gminastromiec
- gminastrykow
- gminastryszawa
- gminastryszow
- gminastrzalkowo
- gminastrzelceopolskie
+ - gminastrzelceopolskie2
- gminastrzelin
- gminastrzelno
- gminastrzyzewice
@@ -2443,6 +2622,7 @@
- gminatarnow
- gminatarnowiec
- gminatarnowopolski
+ - gminatarnowopolski2
- gminateresin
- gminatereszpol
- gminatluchowo
@@ -2467,6 +2647,7 @@
- gminatyrawawoloska
- gminauchanie
- gminaujazd
+ - gminaujazdpsp
- gminaulanmajorat
- gminaulanow
- gminaulez
@@ -2495,6 +2676,7 @@
- gminawielgomlyny
- gminawieliszew
- gminawielkanieszawka
+ - gminawielopoleskrzynskie
- gminawieniawa
- gminawieprz
- gminawieruszow
@@ -2534,6 +2716,7 @@
- gminawolsztyn
- gminawreczycawielka
- gminawronki
+ - gminawyrki
- gminawyrzysk
- gminawysokie
- gminazabno
@@ -2577,6 +2760,7 @@
- gminazolynia
- gminazukowice
- gminazurawica
+ - gminazychlin
- gminazyrakow
- gminazyrzyn
- gminazytno
@@ -2589,9 +2773,11 @@
- gorzno
- gorzowslaski
- gorzowwielkopolski
+ - gorzycepowiattarnobrzeski
- gostynin
- grajewo
- grodziskmazowiecki
+ - grodziskwielkopolski
- grudziadz
- grybow
- gryfino
@@ -2599,10 +2785,14 @@
- hel
- hrubieszow
- inowroclaw
+ - irtarnow
- izbicakujawska
+ - jablonnapowiatlegionowski
+ - jablonnapowiatlubelski
- jablonowopomorskie
- janowiecwielkopolski
- janowlubelski
+ - janowpowiatsokolski
- jarocin
- jaroslaw
- jaslo
@@ -2637,6 +2827,7 @@
- kolobrzeg
- koniecpol
- konin
+ - konopnicapowiatlubelski
- konstancinjeziorna
- konstantynowlodzki
- koronowo
@@ -2657,6 +2848,7 @@
- krosno
- krotoszyce
- krotoszyn
+ - krynica
- krzeszowice
- krzyzwielkopolski
- ksiazwielkopolski
@@ -2676,12 +2868,14 @@
- ledziny
- legionowo
- legnica
+ - lesnicaopolska
- leszno
- lewinbrzeski
- lewinbrzeski2
- lezajsk
- limanowa
- lipno
+ - lipnopowiatlipnowski
- lodz
- lodzkie
- lowicz
@@ -2692,6 +2886,7 @@
- lubin
- lublin
- lubliniec
+ - lubnicepowiatstaszowski
- lubuskie
- lukow
- lwowecki
@@ -2699,18 +2894,30 @@
- malbork
- malopolskie
- marki
+ - maszewopowiatgoleniowski
- mazowieckie
+ - men
+ - miastotorun
- michalowice
- miechow
- miedzyrzecpodlaski
- miejskagorka
- mielec
- milanowek
+ - minrol
- minskmazowiecki
+ - mkdn
- mniszkow
+ - mnsw
+ - mon
+ - mos
- mosina
+ - moszczenicapowiatgorlicki
+ - moszczenicapowiatpiotrkowski
- mragowo
- mragowski
+ - ms
+ - msw
- mszanadolna
- mszczonow
- muszyna
@@ -2734,9 +2941,17 @@
- nowyzmigrod
- nysa
- obornikislaskie
+ - obornikiwielkopolskie
- obrzycko
+ - olesnicapowiatolesnicki
+ - olesnicapowiatstaszowski
+ - olesnopowiatdabrowski
+ - olesnopowiatoleski
- olkusz
+ - olszankapowiatbrzeski
- olsztyn
+ - opatowpowiatklobucki
+ - opatowpowiatopatowski
- opinogoragorna
- opoczno
- opole
@@ -2745,8 +2960,10 @@
- orzesze
- osieczna
- osiecznica
+ - osiekpowiatstarogardzki
- ostroda
- ostroleka
+ - ostrowiecsw
- ostrowwielkopolski
- oswiecim
- otwock
@@ -2763,6 +2980,7 @@
- pilzno
- piotrkowtrybunalski
- pisz
+ - piwniczna
- plock
- plonsk
- pniewy
@@ -2774,6 +2992,8 @@
- pomorskie
- poniec
- poreba
+ - poswietnepowiatopoczynski
+ - poswietnepowiatwolominski
- powiataleksandrowski
- powiataugustowski
- powiatbedzinski
@@ -2814,6 +3034,7 @@
- powiatgizycki
- powiatgliwicki
- powiatglogowski
+ - powiatglubczycki
- powiatgnieznienski
- powiatgoldapski
- powiatgoleniowski
@@ -2823,6 +3044,8 @@
- powiatgorzowski
- powiatgostynski
- powiatgrajewski
+ - powiatgrodziskimazowieckie
+ - powiatgrodziskiwielkopolskie
- powiatgrojecki
- powiatgryficki
- powiatgryfinski
@@ -2890,6 +3113,7 @@
- powiatmakowski
- powiatmalborski
- powiatmiechowski
+ - powiatmiedzyrzecki
- powiatmielecki
- powiatmikolowski
- powiatmilicki
@@ -2918,17 +3142,21 @@
- powiatolsztynski
- powiatopatowski
- powiatopoczynski
+ - powiatopolelubelskie
- powiatopolski
+ - powiatopolski2
- powiatostrodzki
- powiatostrowiecki
- powiatostrzeszowski
- powiatoswiecimski
+ - powiatostrowskimazowieckie
- powiatotwocki
- powiatpabianicki
- powiatpiaseczynski
- powiatpilski
- powiatpinczowski
- powiatpiotrkowski
+ - powiatpiskiwarminskomazurskie
- powiatpleszewski
- powiatplocki
- powiatplonski
@@ -2988,6 +3216,7 @@
- powiatsuski
- powiatswidnicki
- powiatswidwinski
+ - powiatswidnickiwswidniku
- powiatswiebodzinski
- powiatswiecki
- powiatszamotulski
@@ -3000,6 +3229,7 @@
- powiattatrzanski
- powiattczewski
- powiattomaszowski
+ - powiattomaszowskilubelskie
- powiattorunski
- powiattrzebnicki
- powiattucholski
@@ -3041,6 +3271,7 @@
- powiatzyrardowski
- powiatzywiecki
- poznan
+ - prfrawamaz
- proszowice
- prudnik
- pruszczgdanski
@@ -3058,6 +3289,7 @@
- rabkazdroj
- raciaz
- raciborz
+ - radkowklodzki
- radom
- radomsko
- radomyslwielki
@@ -3065,12 +3297,17 @@
- radziejow
- radzionkow
- radzynpodlaski
+ - rakoniewice
- rawamazowiecka
- rawicz
- reda
+ - rejowiecpowiatchelmski
+ - rogowopowiatrypinski
+ - rogowopowiatzninski
- rogozno
- ropczyce
- rudaslaska
+ - rudnikpowiatraciborski
- rumia
- rybnik
- rychwal
@@ -3079,6 +3316,7 @@
- rypin
- rzeszow
- rzeszowprojekt
+ - rzgowpowiatkoninski
- sandomierz
- sanok
- sedziszowmalopolski
@@ -3100,14 +3338,19 @@
- sokolowpodlaski
- sopot
- sosnowiec
+ - spmajkowskarzysko
+ - spteodory
- srem
- srodaslaska
- srodawielkopolska
- starachowice
- stargard
- starogardgdanski
+ - starostwokrosnienskie
- starysacz
- staszow
+ - stezycapowiatrycki
+ - stowarzyszenieintegracja
- stronieslaskie
- strzyzow
- sulejowek
@@ -3115,9 +3358,12 @@
- sulmierzyce
- swarzedz
- swidnica
+ - swidnicapowiatswidnicki
+ - swidnicapowiatzielonogorski
- swidnik
- swidwin
- swieradowzdroj
+ - swietajnopowiatszczycienski
- swietochlowice
- swinoujscie
- sycow
@@ -3129,6 +3375,7 @@
- szprotawa
- sztum
- szubin
+ - szydlowopowiatpilski
- tarnobrzeg
- tarnow
- tarnowskiegory
@@ -3145,6 +3392,7 @@
- turawa
- tuszyn
- tychy
+ - uggolcza
- ujazd
- ustka
- ustron
@@ -3154,6 +3402,20 @@
- walcz
- warminskomazurskie
- warszawa
+ - warszawabemowo
+ - warszawabialoleka
+ - warszawabielany
+ - warszawamokotow
+ - warszawapragapoludnie
+ - warszawasrodmiescie
+ - warszawatargowek
+ - warszawaursus
+ - warszawaursynow
+ - warszawawawer
+ - warszawawesola
+ - warszawawlochy
+ - warszawawola
+ - warszawazoliborz
- wasosz
- wegrow
- wejherowo
@@ -3161,6 +3423,10 @@
- wieliczka
- wielkopolskie
- wielun
+ - wierzbicapowiatchelmski
+ - wierzbicapowiatradomski
+ - wilkowpowiatnamyslowski
+ - wisniowapowiatmyslenicki
- wladyslawowo
- wloclawek
- wlodawa
@@ -3177,20 +3443,30 @@
- zagan
- zakliczyn
- zakopane
+ - zakrzewopowiataleksandrowski
- zambrow
- zamosc
- zary
- zawidow
- zdunskawola
- zduny
+ - zdzwarszawa
- zelechow
+ - zespolszkolppckumarszew
- zgierz
- zgorzelec
- zielonagora
- zielonka
+ - zkso1katowice
- zlotoryja
- zlotow
- zory
+ - zs2lubin
+ - zsksieradz
+ - zskzkwidzyn
+ - zskzsochaczew
+ - zspstarekozle
+ - zstpowiatopoczynski
- zwolen
- zyrardow
From f455064b9d4e2b219033b2500e02dedd08955040 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miko=C5=82aj=20Pich?=
Date: Sat, 2 Mar 2024 21:18:02 +0100
Subject: [PATCH 25/25] Version 2.5.0
---
app/build.gradle | 10 +++++-----
app/src/main/play/release-notes/pl-PL/default.txt | 12 ++++++++----
2 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index f01b9917..6f63715b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -27,8 +27,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 34
- versionCode 148
- versionName "2.4.2"
+ versionCode 149
+ versionName "2.5.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@@ -164,8 +164,8 @@ play {
defaultToAppBundles = false
track = 'production'
releaseStatus = ReleaseStatus.IN_PROGRESS
- userFraction = 0.99d
- updatePriority = 2
+ userFraction = 0.20d
+ updatePriority = 1
enabled.set(false)
}
@@ -195,7 +195,7 @@ ext {
}
dependencies {
- implementation 'io.github.wulkanowy:sdk:2.4.2-SNAPSHOT'
+ implementation 'io.github.wulkanowy:sdk:2.5.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
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 ef6308b6..98c48e15 100644
--- a/app/src/main/play/release-notes/pl-PL/default.txt
+++ b/app/src/main/play/release-notes/pl-PL/default.txt
@@ -1,7 +1,11 @@
-Wersja 2.4.2
+Wersja 2.5.0
-- naprawiliśmy crash przy przełączaniu uczniów, motywów i języków
-- naprawiliśmy crash przy dodawaniu dodatkowych lekcji
-- naprawiliśmy obsługę błędów widżetach
+— dodaliśmy wyświetlanie ogłoszeń
+— dodaliśmy opcję przywracania wiadomości z kosza
+— dodaliśmy opcję wyciszania nadawców wiadomości
+— naprawiliśmy opcjonalne liczenie średniej arytmetycznej, kiedy brak ocen z wagą w drugim semestrze
+— usprawniliśmy ładowanie frekwencji i planu lekcji
+— naprawiliśmy usprawiedliwianie nieobecności i autoryzację u użytkowników eduOne
+— zmieniliśmy komunikat o zmienionym haśle
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases