diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a146711d..6e8fc491 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,7 +24,6 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:networkSecurityConfig="@xml/network_security_config" - android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme.Dark" android:usesCleartextTraffic="true" diff --git a/app/src/main/assets/pl-changelog.html b/app/src/main/assets/pl-changelog.html index 3fa725f2..4929ebcc 100644 --- a/app/src/main/assets/pl-changelog.html +++ b/app/src/main/assets/pl-changelog.html @@ -1,30 +1,8 @@ -

Wersja 4.0, 2020-04-19

+

Wersja 4.1, 2020-05-09



diff --git a/app/src/main/cpp/szkolny-signing.cpp b/app/src/main/cpp/szkolny-signing.cpp index 6463e8af..f06d86b7 100644 --- a/app/src/main/cpp/szkolny-signing.cpp +++ b/app/src/main/cpp/szkolny-signing.cpp @@ -9,7 +9,7 @@ /*secret password - removed for source code publication*/ static toys AES_IV[16] = { - 0x38, 0xd4, 0x73, 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + 0xec, 0xc0, 0x3e, 0x4e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; unsigned char *agony(unsigned int laugh, unsigned char *box, unsigned char *heat); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/App.kt b/app/src/main/java/pl/szczodrzynski/edziennik/App.kt index 7194aa04..3a9c0725 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/App.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/App.kt @@ -66,6 +66,7 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope { val timetableManager by lazy { TimetableManager(this) } val eventManager by lazy { EventManager(this) } val permissionManager by lazy { PermissionManager(this) } + val attendanceManager by lazy { AttendanceManager(this) } val db get() = App.db @@ -100,9 +101,9 @@ class App : MultiDexApplication(), Configuration.Provider, CoroutineScope { .followSslRedirects(true) .retryOnConnectionFailure(true) .cookieJar(cookieJar) - .connectTimeout(20, TimeUnit.SECONDS) - .writeTimeout(5, TimeUnit.SECONDS) - .readTimeout(10, TimeUnit.SECONDS) + .connectTimeout(15, TimeUnit.SECONDS) + .writeTimeout(10, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) builder.installHttpsSupport(this) if (debugMode || BuildConfig.DEBUG) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt index 19b2886d..3033f0f0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/MainActivity.kt @@ -18,6 +18,7 @@ import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.PopupMenu import androidx.core.graphics.ColorUtils +import androidx.core.view.isVisible import androidx.lifecycle.Observer import androidx.navigation.NavOptions import com.danimahardhika.cafebar.CafeBar @@ -294,6 +295,13 @@ class MainActivity : AppCompatActivity(), CoroutineScope { mainSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar) errorSnackbar.setCoordinator(b.navView.coordinator, b.navView.bottomBar) + if (BuildConfig.VERSION_NAME.contains("nightly")) { + b.nightlyText.isVisible = true + b.nightlyText.text = "Nightly\n"+BuildConfig.VERSION_NAME.substringAfterLast(".") + } + else + b.nightlyText.isVisible = false + navLoading = true b.navView.apply { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfig.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfig.kt index d956c1ec..c4c65ac3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfig.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfig.kt @@ -30,6 +30,7 @@ class ProfileConfig(val db: AppDb, val profileId: Int, rawEntries: List(.+?)""".toRegex(DOT_MATCHES_ALL) + } val MOBIDZIENNIK_ATTENDANCE_TABLE by lazy { """(.+?)
""".toRegex(DOT_MATCHES_ALL) } @@ -81,7 +84,7 @@ object Regexes { """([0-9:]+) - .+? (.+?)""".toRegex(DOT_MATCHES_ALL) } val MOBIDZIENNIK_ATTENDANCE_LESSON by lazy { - """(.+?) - (.*?).+?.+?\((.+?), .+?(.+?)\)""".toRegex(DOT_MATCHES_ALL) + """(.+?)\s*\s*\((.+?),\s*(.+?)\)""".toRegex(DOT_MATCHES_ALL) } val MOBIDZIENNIK_HOMEWORK_ROW by lazy { @@ -151,6 +154,9 @@ object Regexes { val LIBRUS_ATTACHMENT_KEY by lazy { """singleUseKey=([0-9A-z_]+)""".toRegex() } + val LIBRUS_MESSAGE_ID by lazy { + """/wiadomosci/[0-9]+/[0-9]+/([0-9]+?)/""".toRegex() + } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAnnouncements.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAnnouncements.kt index 11f048b5..32ca353c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAnnouncements.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAnnouncements.kt @@ -45,24 +45,25 @@ class EdudziennikWebAnnouncements(override val data: DataEdudziennik, val addedDate = Date.fromIsoHm(dateString) val announcementObject = Announcement( - profileId, - id, - subject, - null, - startDate, - null, - teacher.id, - longId - ) + profileId = profileId, + id = id, + subject = subject, + text = null, + startDate = startDate, + endDate = null, + teacherId = teacher.id, + addedDate = addedDate + ).also { + it.idString = longId + } - data.announcementIgnoreList.add(announcementObject) + data.announcementList.add(announcementObject) data.metadataList.add(Metadata( profileId, Metadata.TYPE_ANNOUNCEMENT, id, profile.empty, - profile.empty, - addedDate + profile.empty )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAttendance.kt index 92545117..7fe18171 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAttendance.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebAttendance.kt @@ -39,12 +39,12 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik, val attendanceTypes = EDUDZIENNIK_ATTENDANCE_TYPES.find(text)?.get(1)?.split(',')?.map { val type = EDUDZIENNIK_ATTENDANCE_TYPE.find(it.trim()) - val symbol = type?.get(1)?.trim() - val name = type?.get(2)?.trim() + val symbol = type?.get(1)?.trim() ?: "?" + val name = type?.get(2)?.trim() ?: "nieznany rodzaj" return@map Triple( symbol, name, - when (name?.toLowerCase(Locale.ROOT)) { + when (name.toLowerCase(Locale.ROOT)) { "obecność" -> Attendance.TYPE_PRESENT "nieobecność" -> Attendance.TYPE_ABSENT "spóźnienie" -> Attendance.TYPE_BELATED @@ -52,7 +52,7 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik, "dzień wolny" -> Attendance.TYPE_DAY_FREE "brak zajęć" -> Attendance.TYPE_DAY_FREE "oddelegowany" -> Attendance.TYPE_RELEASED - else -> Attendance.TYPE_CUSTOM + else -> Attendance.TYPE_UNKNOWN } ) } ?: emptyList() @@ -62,38 +62,42 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik, val lessonNumber = attendanceElement[2].toInt() val attendanceSymbol = attendanceElement[3] - val lessons = data.app.db.timetableDao().getForDateNow(profileId, date) + val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date) val lesson = lessons.firstOrNull { it.lessonNumber == lessonNumber } val id = "${date.stringY_m_d}:$lessonNumber:$attendanceSymbol".crc32() - val (_, name, type) = attendanceTypes.firstOrNull { (symbol, _, _) -> symbol == attendanceSymbol } + val (typeSymbol, typeName, baseType) = attendanceTypes.firstOrNull { (symbol, _, _) -> symbol == attendanceSymbol } ?: return@forEach val startTime = data.lessonRanges.singleOrNull { it.lessonNumber == lessonNumber }?.startTime ?: return@forEach val attendanceObject = Attendance( - profileId, - id, - lesson?.displayTeacherId ?: -1, - lesson?.displaySubjectId ?: -1, - profile.currentSemester, - name, - date, - lesson?.displayStartTime ?: startTime, - type - ) + profileId = profileId, + id = id, + baseType = baseType, + typeName = typeName, + typeShort = data.app.attendanceManager.getTypeShort(baseType), + typeSymbol = typeSymbol, + typeColor = null, + date = date, + startTime = lesson?.displayStartTime ?: startTime, + semester = profile.currentSemester, + teacherId = lesson?.displayTeacherId ?: -1, + subjectId = lesson?.displaySubjectId ?: -1 + ).also { + it.lessonNumber = lessonNumber + } data.attendanceList.add(attendanceObject) - if(type != Attendance.TYPE_PRESENT) { + if (baseType != Attendance.TYPE_PRESENT) { data.metadataList.add(Metadata( profileId, Metadata.TYPE_ATTENDANCE, id, - profile.empty, - profile.empty, - System.currentTimeMillis() + profile.empty || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN, + profile.empty || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebEvents.kt index 52833b9a..35c68478 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebEvents.kt @@ -57,8 +57,7 @@ class EdudziennikWebEvents(override val data: DataEdudziennik, Metadata.TYPE_EVENT, id, profile.empty, - profile.empty, - System.currentTimeMillis() + profile.empty )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebExams.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebExams.kt index 8e7836fd..f6abf637 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebExams.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebExams.kt @@ -46,7 +46,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik, if (dateString.isBlank()) return@forEach val date = Date.fromY_m_d(dateString) - val lessons = data.app.db.timetableDao().getForDateNow(profileId, date) + val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date) val startTime = lessons.firstOrNull { it.displaySubjectId == subject.id }?.displayStartTime val eventTypeElement = examElement.child(3).child(0) @@ -74,8 +74,7 @@ class EdudziennikWebExams(override val data: DataEdudziennik, Metadata.TYPE_EVENT, id, profile.empty, - profile.empty, - System.currentTimeMillis() + profile.empty )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGrades.kt index 50557333..459f13aa 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebGrades.kt @@ -126,7 +126,8 @@ class EdudziennikWebGrades(override val data: DataEdudziennik, comment = null, semester = semester, teacherId = teacher.id, - subjectId = subject.id + subjectId = subject.id, + addedDate = addedDate ) data.gradeList.add(gradeObject) @@ -135,8 +136,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik, Metadata.TYPE_GRADE, id, profile.empty, - profile.empty, - addedDate + profile.empty )) } @@ -168,8 +168,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik, Metadata.TYPE_GRADE, proposedGradeObject.id, profile.empty, - profile.empty, - System.currentTimeMillis() + profile.empty )) } @@ -201,8 +200,7 @@ class EdudziennikWebGrades(override val data: DataEdudziennik, Metadata.TYPE_GRADE, finalGradeObject.id, profile.empty, - profile.empty, - System.currentTimeMillis() + profile.empty )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebHomework.kt index acabcfa6..169a5127 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebHomework.kt @@ -43,7 +43,7 @@ class EdudziennikWebHomework(override val data: DataEdudziennik, val subjectName = subjectElement.text() val subject = data.getSubject(subjectId, subjectName) - val lessons = data.app.db.timetableDao().getForDateNow(profileId, date) + val lessons = data.app.db.timetableDao().getAllForDateNow(profileId, date) val startTime = lessons.firstOrNull { it.subjectId == subject.id }?.displayStartTime val teacherName = homeworkElement.child(2).text() @@ -72,8 +72,7 @@ class EdudziennikWebHomework(override val data: DataEdudziennik, Metadata.TYPE_HOMEWORK, id, profile.empty, - profile.empty, - System.currentTimeMillis() + profile.empty )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebLuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebLuckyNumber.kt index 70c6849a..db8164c8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebLuckyNumber.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebLuckyNumber.kt @@ -24,9 +24,9 @@ class EdudziennikWebLuckyNumber(override val data: DataEdudziennik, webGet(TAG, data.schoolEndpoint + "Lucky", xhr = true) { text -> text.toIntOrNull()?.also { luckyNumber -> val luckyNumberObject = LuckyNumber( - profileId, - Date.getToday(), - luckyNumber + profileId = profileId, + date = Date.getToday(), + number = luckyNumber ) data.luckyNumberList.add(luckyNumberObject) @@ -35,8 +35,7 @@ class EdudziennikWebLuckyNumber(override val data: DataEdudziennik, Metadata.TYPE_LUCKY_NUMBER, luckyNumberObject.date.value.toLong(), true, - profile.empty, - System.currentTimeMillis() + profile.empty )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebNotes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebNotes.kt index a111ef79..35a78168 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebNotes.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebNotes.kt @@ -41,12 +41,15 @@ class EdudziennikWebNotes(override val data: DataEdudziennik, val description = noteElement.child(3).text() val noticeObject = Notice( - profileId, - id, - description, - profile.currentSemester, - Notice.TYPE_NEUTRAL, - teacher.id + profileId = profileId, + id = id, + type = Notice.TYPE_NEUTRAL, + semester = profile.currentSemester, + text = description, + category = null, + points = null, + teacherId = teacher.id, + addedDate = addedDate ) data.noticeList.add(noticeObject) @@ -55,8 +58,7 @@ class EdudziennikWebNotes(override val data: DataEdudziennik, Metadata.TYPE_NOTICE, id, profile.empty, - profile.empty, - addedDate + profile.empty )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebTimetable.kt index 3a31e208..94db15ef 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebTimetable.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/data/web/EdudziennikWebTimetable.kt @@ -124,8 +124,7 @@ class EdudziennikWebTimetable(override val data: DataEdudziennik, Metadata.TYPE_LESSON_CHANGE, lessonObject.id, seen, - seen, - System.currentTimeMillis() + seen )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiCurrentRegister.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiCurrentRegister.kt index 0e000fb1..d5e59596 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiCurrentRegister.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiCurrentRegister.kt @@ -68,9 +68,9 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik, val luckyNumberObject = LuckyNumber( - data.profileId, - luckyNumberDate, - luckyNumber + profileId = data.profileId, + date = luckyNumberDate, + number = luckyNumber ) data.luckyNumberList.add(luckyNumberObject) @@ -80,8 +80,7 @@ class IdziennikApiCurrentRegister(override val data: DataIdziennik, Metadata.TYPE_LUCKY_NUMBER, luckyNumberObject.date.value.toLong(), true, - data.profile?.empty ?: false, - System.currentTimeMillis() + data.profile?.empty ?: false )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesInbox.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesInbox.kt index 58e7660c..622068e2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesInbox.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesInbox.kt @@ -69,7 +69,8 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik, type = if (jMessage.getBoolean("rekordUsuniety") == true) TYPE_DELETED else TYPE_RECEIVED, subject = subject, body = body, - senderId = rTeacher.id + senderId = rTeacher.id, + addedDate = sentDate ) val messageRecipient = MessageRecipient( @@ -87,8 +88,7 @@ class IdziennikApiMessagesInbox(override val data: DataIdziennik, Metadata.TYPE_MESSAGE, message.id, readDate > 0, - readDate > 0 || profile?.empty ?: false, - sentDate + readDate > 0 || profile?.empty ?: false )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesSent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesSent.kt index 286c9896..87b3264d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesSent.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/api/IdziennikApiMessagesSent.kt @@ -51,7 +51,8 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik, type = TYPE_SENT, subject = subject, body = body, - senderId = null + senderId = null, + addedDate = sentDate ) for (recipientEl in jMessage.getAsJsonArray("odbiorcy")) { @@ -76,7 +77,7 @@ class IdziennikApiMessagesSent(override val data: DataIdziennik, } data.messageList.add(message) - data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, sentDate)) + data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true)) } data.setSyncNext(ENDPOINT_IDZIENNIK_API_MESSAGES_SENT, DAY, DRAWER_ITEM_MESSAGES) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAnnouncements.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAnnouncements.kt index f8744efa..531cbeeb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAnnouncements.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAnnouncements.kt @@ -52,14 +52,14 @@ class IdziennikWebAnnouncements(override val data: DataIdziennik, val startDate = jAnnouncement.getString("DataWydarzenia")?.replace("[^\\d]".toRegex(), "")?.toLongOrNull()?.let { Date.fromMillis(it) } val announcementObject = Announcement( - profileId, - announcementId, - jAnnouncement.get("Temat").asString, - jAnnouncement.get("Tresc").asString, - startDate, - null, - rTeacher.id, - null + profileId = profileId, + id = announcementId, + subject = jAnnouncement.get("Temat").asString, + text = jAnnouncement.get("Tresc").asString, + startDate = startDate, + endDate = null, + teacherId = rTeacher.id, + addedDate = addedDate ) data.announcementList.add(announcementObject) data.metadataList.add(Metadata( @@ -67,8 +67,7 @@ class IdziennikWebAnnouncements(override val data: DataIdziennik, Metadata.TYPE_ANNOUNCEMENT, announcementObject.id, profile?.empty ?: false, - profile?.empty ?: false, - addedDate + profile?.empty ?: false )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAttendance.kt index aa0a9998..e77c9eac 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAttendance.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebAttendance.kt @@ -12,10 +12,18 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.ENDPOINT_IDZIENNI import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.db.entity.Attendance -import pl.szczodrzynski.edziennik.data.db.entity.Attendance.* +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT_EXCUSED +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_BELATED +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT_CUSTOM +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_RELEASED +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_UNKNOWN import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS +import pl.szczodrzynski.edziennik.getInt import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time @@ -51,71 +59,97 @@ class IdziennikWebAttendance(override val data: DataIdziennik, for (jAttendanceEl in json.getAsJsonArray("Obecnosci")) { val jAttendance = jAttendanceEl.asJsonObject // jAttendance - val attendanceTypeIdziennik = jAttendance.get("TypObecnosci").asInt - if (attendanceTypeIdziennik == 5 || attendanceTypeIdziennik == 7) - continue - val attendanceDate = Date.fromY_m_d(jAttendance.get("Data").asString) - val attendanceTime = Time.fromH_m(jAttendance.get("OdDoGodziny").asString) - if (attendanceDate.combineWith(attendanceTime) > System.currentTimeMillis()) + val type = jAttendance.get("TypObecnosci").asInt + + // skip "zajęcia nie odbyły się" and "Ferie" + if (type == 5 || type == 7) continue - val attendanceId = jAttendance.get("IdLesson").asString.crc16().toLong() + val date = Date.fromY_m_d(jAttendance.get("Data").asString) + val time = Time.fromH_m(jAttendance.get("OdDoGodziny").asString) + if (date.combineWith(time) > System.currentTimeMillis()) + continue + + val id = jAttendance.get("IdLesson").asString.crc16().toLong() val rSubject = data.getSubject(jAttendance.get("Przedmiot").asString, jAttendance.get("IdPrzedmiot").asLong, "") val rTeacher = data.getTeacherByFDotSpaceLast(jAttendance.get("PrzedmiotNauczyciel").asString) - var attendanceName = "obecność" - var attendanceType = Attendance.TYPE_CUSTOM + var baseType = TYPE_UNKNOWN + var typeName = "nieznany rodzaj" + var typeSymbol: String? = null + var typeColor: Long? = null - when (attendanceTypeIdziennik) { - 1 /* nieobecność usprawiedliwiona */ -> { - attendanceName = "nieobecność usprawiedliwiona" - attendanceType = TYPE_ABSENT_EXCUSED + /* https://iuczniowie.progman.pl/idziennik/mod_panelRodzica/obecnosci/obecnosciUcznia_lmt637231494660000000.js */ + /* https://iuczniowie.progman.pl/idziennik/mod_panelRodzica/obecnosci/obecnosci_lmt637231494660000000.css */ + when (type) { + 1 -> { + baseType = TYPE_ABSENT_EXCUSED + typeName = "nieobecność usprawiedliwiona" + typeColor = 0xffffe099 } - 2 /* spóźnienie */ -> { - attendanceName = "spóźnienie" - attendanceType = TYPE_BELATED + 2 -> { + baseType = TYPE_BELATED + typeName = "spóźnienie" + typeColor = 0xffffffaa } - 3 /* nieobecność nieusprawiedliwiona */ -> { - attendanceName = "nieobecność nieusprawiedliwiona" - attendanceType = TYPE_ABSENT + 3 -> { + baseType = TYPE_ABSENT + typeName = "nieobecność nieusprawiedliwiona" + typeColor = 0xffffad99 } - 4 /* zwolnienie */, 9 /* zwolniony / obecny */ -> { - attendanceType = TYPE_RELEASED - if (attendanceTypeIdziennik == 4) - attendanceName = "zwolnienie" - if (attendanceTypeIdziennik == 9) - attendanceName = "zwolnienie / obecność" + 4, 9 -> { + baseType = TYPE_RELEASED + if (type == 4) { + typeName = "zwolnienie" + typeColor = 0xffa8beff + } + if (type == 9) { + typeName = "zwolniony / obecny" + typeSymbol = "zb" + typeColor = 0xffff69b4 + } } - 0 /* obecny */, 8 /* Wycieczka */ -> { - attendanceType = TYPE_PRESENT - if (attendanceTypeIdziennik == 8) - attendanceName = "wycieczka" + 8 -> { + baseType = TYPE_PRESENT_CUSTOM + typeName = "wycieczka" + typeSymbol = "w" + typeColor = null + } + 0 -> { + baseType = TYPE_PRESENT + typeName = "obecny" + typeColor = 0xffccffcc } } - val semester = profile?.dateToSemester(attendanceDate) ?: 1 + val semester = profile?.dateToSemester(date) ?: 1 val attendanceObject = Attendance( - profileId, - attendanceId, - rTeacher.id, - rSubject.id, - semester, - attendanceName, - attendanceDate, - attendanceTime, - attendanceType - ) + profileId = profileId, + id = id, + baseType = baseType, + typeName = typeName, + typeShort = typeSymbol ?: data.app.attendanceManager.getTypeShort(baseType), + typeSymbol = typeSymbol ?: data.app.attendanceManager.getTypeShort(baseType), + typeColor = typeColor?.toInt(), + date = date, + startTime = time, + semester = semester, + teacherId = rTeacher.id, + subjectId = rSubject.id + ).also { + it.lessonTopic = jAttendance.getString("PrzedmiotTemat") + it.lessonNumber = jAttendance.getInt("Godzina") + } data.attendanceList.add(attendanceObject) - if (attendanceObject.type != TYPE_PRESENT) { + if (attendanceObject.baseType != TYPE_PRESENT) { data.metadataList.add(Metadata( profileId, Metadata.TYPE_ATTENDANCE, attendanceObject.id, - profile?.empty ?: false, - profile?.empty ?: false, - System.currentTimeMillis() + profile?.empty ?: false || baseType == TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN, + profile?.empty ?: false || baseType == TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebExams.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebExams.kt index 82e63e79..25c06945 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebExams.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebExams.kt @@ -68,7 +68,7 @@ class IdziennikWebExams(override val data: DataIdziennik, val teacherId = data.getTeacherByLastFirst(teacherName).id val topic = exam.getString("zakres")?.trim() ?: "" - val lessonList = data.db.timetableDao().getForDateNow(profileId, examDate) + val lessonList = data.db.timetableDao().getAllForDateNow(profileId, examDate) val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime val eventType = when (exam.getString("rodzaj")?.toLowerCase(Locale.getDefault())) { @@ -98,8 +98,7 @@ class IdziennikWebExams(override val data: DataIdziennik, Metadata.TYPE_EVENT, eventObject.id, profile?.empty ?: false, - profile?.empty ?: false, - System.currentTimeMillis() + profile?.empty ?: false )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetMessage.kt index e709122d..bd1808df 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetMessage.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetMessage.kt @@ -94,8 +94,7 @@ class IdziennikWebGetMessage(override val data: DataIdziennik, Metadata.TYPE_MESSAGE, message.id, message.seen, - message.notified, - message.addedDate + message.notified )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGrades.kt index b3237c82..90e17401 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGrades.kt @@ -63,6 +63,8 @@ class IdziennikWebGrades(override val data: DataIdziennik, colorInt = Color.parseColor("#$gradeColor") } + val addedDate = grade.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis() + val gradeObject = Grade( profileId = profileId, id = id, @@ -76,7 +78,9 @@ class IdziennikWebGrades(override val data: DataIdziennik, comment = null, semester = semester, teacherId = teacher.id, - subjectId = subject.id) + subjectId = subject.id, + addedDate = addedDate + ) when (grade.getInt("Typ")) { 0 -> { @@ -100,6 +104,8 @@ class IdziennikWebGrades(override val data: DataIdziennik, colorInt = Color.parseColor("#$historyColor") } + val addedDate = historyItem.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis() + val historyObject = Grade( profileId = profileId, id = gradeObject.id * -1, @@ -113,19 +119,18 @@ class IdziennikWebGrades(override val data: DataIdziennik, comment = null, semester = historyItem.getInt("Semestr") ?: 1, teacherId = teacher.id, - subjectId = subject.id) + subjectId = subject.id, + addedDate = addedDate + ) historyObject.parentId = gradeObject.id - val addedDate = historyItem.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis() - data.gradeList.add(historyObject) data.metadataList.add(Metadata( profileId, Metadata.TYPE_GRADE, historyObject.id, true, - true, - addedDate + true )) } // update the current grade's value with an average of all historical grades and itself @@ -147,8 +152,6 @@ class IdziennikWebGrades(override val data: DataIdziennik, } } - val addedDate = grade.getString("Data_wystaw")?.let { Date.fromY_m_d(it).inMillis } ?: System.currentTimeMillis() - data.gradeList.add(gradeObject) data.metadataList.add( Metadata( @@ -156,8 +159,7 @@ class IdziennikWebGrades(override val data: DataIdziennik, Metadata.TYPE_GRADE, id, data.profile.empty, - data.profile.empty, - addedDate + data.profile.empty )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebHomework.kt index 2289e13d..10b4a0c3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebHomework.kt @@ -57,7 +57,7 @@ class IdziennikWebHomework(override val data: DataIdziennik, val subjectId = data.getSubject(subjectName, null, subjectName).id val teacherName = homework.getString("usr") ?: return@forEach val teacherId = data.getTeacherByLastFirst(teacherName).id - val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate) + val lessonList = data.db.timetableDao().getAllForDateNow(profileId, eventDate) val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.displayStartTime val topic = homework.getString("tytul")?.trim() ?: "" @@ -77,7 +77,8 @@ class IdziennikWebHomework(override val data: DataIdziennik, type = Event.TYPE_HOMEWORK, teacherId = teacherId, subjectId = subjectId, - teamId = data.teamClass?.id ?: -1 + teamId = data.teamClass?.id ?: -1, + addedDate = addedDate.inMillis ) data.eventList.add(eventObject) @@ -86,8 +87,7 @@ class IdziennikWebHomework(override val data: DataIdziennik, Metadata.TYPE_HOMEWORK, eventObject.id, seen, - seen, - addedDate.inMillis + seen )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebNotices.kt index dcc560d5..64108bfd 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebNotices.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebNotices.kt @@ -13,9 +13,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Notice -import pl.szczodrzynski.edziennik.data.db.entity.Notice.* +import pl.szczodrzynski.edziennik.data.db.entity.Notice.Companion.TYPE_NEGATIVE +import pl.szczodrzynski.edziennik.data.db.entity.Notice.Companion.TYPE_NEUTRAL +import pl.szczodrzynski.edziennik.data.db.entity.Notice.Companion.TYPE_POSITIVE import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.getJsonObject +import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.utils.models.Date class IdziennikWebNotices(override val data: DataIdziennik, @@ -53,20 +56,24 @@ class IdziennikWebNotices(override val data: DataIdziennik, } val noticeObject = Notice( - profileId, - noticeId, - jNotice.get("Tresc").asString, - jNotice.get("Semestr").asInt, - nType, - rTeacher.id) + profileId = profileId, + id = noticeId, + type = nType, + semester = jNotice.get("Semestr").asInt, + text = jNotice.getString("Tresc") ?: "", + category = null, + points = null, + teacherId = rTeacher.id, + addedDate = addedDate.inMillis + ) + data.noticeList.add(noticeObject) data.metadataList.add(Metadata( profileId, Metadata.TYPE_NOTICE, noticeObject.id, profile?.empty ?: false, - profile?.empty ?: false, - addedDate.inMillis + profile?.empty ?: false )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebProposedGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebProposedGrades.kt index c2639e51..b23e9f98 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebProposedGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebProposedGrades.kt @@ -76,6 +76,11 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik, else -1 if (semester1Proposed != "") { + val addedDate = if (data.profile.empty) + data.profile.dateSemester1Start.inMillis + else + System.currentTimeMillis() + val gradeObject = Grade( profileId = profileId, id = semester1Id, @@ -89,26 +94,26 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik, comment = null, semester = 1, teacherId = -1, - subjectId = subjectObject.id + subjectId = subjectObject.id, + addedDate = addedDate ) - val addedDate = if (data.profile.empty) - data.profile.dateSemester1Start.inMillis - else - System.currentTimeMillis() - data.gradeList.add(gradeObject) data.metadataList.add(Metadata( profileId, Metadata.TYPE_GRADE, gradeObject.id, profile.empty, - profile.empty, - addedDate + profile.empty )) } if (semester2Proposed != "") { + val addedDate = if (data.profile.empty) + data.profile.dateSemester2Start.inMillis + else + System.currentTimeMillis() + val gradeObject = Grade( profileId = profileId, id = semester2Id, @@ -122,22 +127,17 @@ class IdziennikWebProposedGrades(override val data: DataIdziennik, comment = null, semester = 2, teacherId = -1, - subjectId = subjectObject.id + subjectId = subjectObject.id, + addedDate = addedDate ) - val addedDate = if (data.profile.empty) - data.profile.dateSemester2Start.inMillis - else - System.currentTimeMillis() - data.gradeList.add(gradeObject) data.metadataList.add(Metadata( profileId, Metadata.TYPE_GRADE, gradeObject.id, profile.empty, - profile.empty, - addedDate + profile.empty )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebSendMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebSendMessage.kt index 973bc74f..343ebcac 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebSendMessage.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebSendMessage.kt @@ -59,7 +59,7 @@ class IdziennikWebSendMessage(override val data: DataIdziennik, IdziennikApiMessagesSent(data, null) { val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject } val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id } - val event = MessageSentEvent(data.profileId, message, metadata?.addedDate) + val event = MessageSentEvent(data.profileId, message, message?.addedDate) EventBus.getDefault().postSticky(event) onSuccess() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebTimetable.kt index 066346cd..e7d293c4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebTimetable.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebTimetable.kt @@ -165,8 +165,7 @@ class IdziennikWebTimetable(override val data: DataIdziennik, Metadata.TYPE_LESSON_CHANGE, lessonObject.id, seen, - seen, - System.currentTimeMillis() + seen )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/login/IdziennikLoginWeb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/login/IdziennikLoginWeb.kt index e15f9410..2c6ad0e7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/login/IdziennikLoginWeb.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/login/IdziennikLoginWeb.kt @@ -74,9 +74,9 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) { Regexes.IDZIENNIK_WEB_LUCKY_NUMBER.find(text)?.also { val number = it[1].toIntOrNull() ?: return@also val luckyNumberObject = LuckyNumber( - data.profileId, - Date.getToday(), - number + profileId = data.profileId, + date = Date.getToday(), + number = number ) data.luckyNumberList.add(luckyNumberObject) @@ -86,8 +86,7 @@ class IdziennikLoginWeb(val data: DataIdziennik, val onSuccess: () -> Unit) { Metadata.TYPE_LUCKY_NUMBER, luckyNumberObject.date.value.toLong(), true, - profile.empty, - System.currentTimeMillis() + profile.empty )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/DataLibrus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/DataLibrus.kt index 57bc112c..e77dd50d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/DataLibrus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/DataLibrus.kt @@ -275,4 +275,10 @@ class DataLibrus(app: App, profile: Profile?, loginStore: LoginStore) : Data(app var timetableNotPublic: Boolean get() { mTimetableNotPublic = mTimetableNotPublic ?: profile?.getStudentData("timetableNotPublic", false); return mTimetableNotPublic ?: false } set(value) { profile?.putStudentData("timetableNotPublic", value) ?: return; mTimetableNotPublic = value } + + /** + * Set to false when Recaptcha helper doesn't provide a working token. + * When it's set to false uses Synergia for messages. + */ + var messagesLoginSuccessful: Boolean = true } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/Librus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/Librus.kt index a79a66f8..48f308f8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/Librus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/Librus.kt @@ -13,9 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.Librus import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetMessage import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetRecipientList import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesSendMessage -import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaGetHomework -import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaHomeworkGetAttachment -import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaMarkAllAnnouncementsAsRead +import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.* import pl.szczodrzynski.edziennik.data.api.edziennik.librus.firstlogin.LibrusFirstLogin import pl.szczodrzynski.edziennik.data.api.edziennik.librus.login.LibrusLogin import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback @@ -91,9 +89,8 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va override fun getMessage(message: MessageFull) { login(LOGIN_METHOD_LIBRUS_MESSAGES) { - LibrusMessagesGetMessage(data, message) { - completed() - } + if (data.messagesLoginSuccessful) LibrusMessagesGetMessage(data, message) { completed() } + else LibrusSynergiaGetMessage(data, message) { completed() } } } @@ -124,10 +121,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) { when (owner) { is Message -> { - login(LOGIN_METHOD_LIBRUS_MESSAGES) { - LibrusMessagesGetAttachment(data, owner, attachmentId, attachmentName) { - completed() - } + login(LOGIN_METHOD_LIBRUS_SYNERGIA) { + if (data.messagesLoginSuccessful) LibrusMessagesGetAttachment(data, owner, attachmentId, attachmentName) { completed() } + LibrusSynergiaGetAttachment(data, owner, attachmentId, attachmentName) { completed() } } } is EventFull -> { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusFeatures.kt index 98caab4e..c14a00df 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusFeatures.kt @@ -50,6 +50,8 @@ const val ENDPOINT_LIBRUS_API_CLASS_FREE_DAYS = 1130 const val ENDPOINT_LIBRUS_SYNERGIA_INFO = 2010 const val ENDPOINT_LIBRUS_SYNERGIA_GRADES = 2020 const val ENDPOINT_LIBRUS_SYNERGIA_HOMEWORK = 2030 +const val ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_RECEIVED = 2040 +const val ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_SENT = 2050 const val ENDPOINT_LIBRUS_MESSAGES_RECEIVED = 3010 const val ENDPOINT_LIBRUS_MESSAGES_SENT = 3020 const val ENDPOINT_LIBRUS_MESSAGES_TRASH = 3030 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusRecaptchaHelper.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusRecaptchaHelper.kt new file mode 100644 index 00000000..94ab5ed1 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/LibrusRecaptchaHelper.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-8. + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.librus + +import android.content.Context +import android.webkit.WebView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import pl.szczodrzynski.edziennik.startCoroutineTimer +import kotlin.coroutines.CoroutineContext + +class LibrusRecaptchaHelper( + val context: Context, + url: String, + html: String, + val onSuccess: (url: String) -> Unit, + val onTimeout: () -> Unit +) : CoroutineScope { + companion object { + private const val TAG = "LibrusRecaptchaHelper" + } + + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Default + + private val webView by lazy { + WebView(context).also { + it.settings.javaScriptEnabled = true + it.webViewClient = WebViewClient() + } + } + + private var timeout: Job? = null + + inner class WebViewClient : android.webkit.WebViewClient() { + override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { + timeout?.cancel() + onSuccess(url) + return true + } + } + + init { + launch(Dispatchers.Main) { + webView.loadDataWithBaseURL(url, html, "text/html", "UTF-8", null) + } + timeout = startCoroutineTimer(delayMillis = 10000L) { + onTimeout() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusData.kt index bf1fdab4..877d803b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusData.kt @@ -8,6 +8,7 @@ import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.data.api.edziennik.librus.* import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.api.* import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusMessagesGetList +import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaGetMessages import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaHomework import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia.LibrusSynergiaInfo import pl.szczodrzynski.edziennik.data.db.entity.Message @@ -201,17 +202,27 @@ class LibrusData(val data: DataLibrus, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_student_info) LibrusSynergiaInfo(data, lastSync, onSuccess) } + ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_RECEIVED -> { + data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) + LibrusSynergiaGetMessages(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess) + } + ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_SENT -> { + data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox) + LibrusSynergiaGetMessages(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess) + } /** * MESSAGES */ ENDPOINT_LIBRUS_MESSAGES_RECEIVED -> { data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) - LibrusMessagesGetList(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess) + if (data.messagesLoginSuccessful) LibrusMessagesGetList(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess) + else LibrusSynergiaGetMessages(data, type = Message.TYPE_RECEIVED, lastSync = lastSync, onSuccess = onSuccess) } ENDPOINT_LIBRUS_MESSAGES_SENT -> { data.startProgress(R.string.edziennik_progress_endpoint_messages_outbox) - LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess) + if (data.messagesLoginSuccessful) LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess) + else LibrusSynergiaGetMessages(data, type = Message.TYPE_SENT, lastSync = lastSync, onSuccess = onSuccess) } else -> onSuccess(endpointId) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusSynergia.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusSynergia.kt index 96f28a65..72faf801 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusSynergia.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/LibrusSynergia.kt @@ -91,6 +91,8 @@ open class LibrusSynergia(open val data: DataLibrus, open val lastSync: Long?) { } fun redirectUrlGet(tag: String, url: String, onSuccess: (url: String) -> Unit) { + d(tag, "Request: Librus/Synergia - $url") + val callback = object : TextCallbackHandler() { override fun onSuccess(text: String?, response: Response) { val redirectUrl = response.headers().get("Location") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncementMarkAsRead.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncementMarkAsRead.kt index d40c1a9a..23cfceb4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncementMarkAsRead.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncementMarkAsRead.kt @@ -37,8 +37,7 @@ class LibrusApiAnnouncementMarkAsRead(override val data: DataLibrus, Metadata.TYPE_ANNOUNCEMENT, announcement.id, announcement.seen, - announcement.notified, - announcement.addedDate + announcement.notified )) onSuccess() } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncements.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncements.kt index 8256b859..d19be231 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncements.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAnnouncements.kt @@ -38,15 +38,17 @@ class LibrusApiAnnouncements(override val data: DataLibrus, val read = announcement.getBoolean("WasRead") ?: false val announcementObject = Announcement( - profileId, - id, - subject, - text, - startDate, - endDate, - teacherId, - longId - ) + profileId = profileId, + id = id, + subject = subject, + text = text, + startDate = startDate, + endDate = endDate, + teacherId = teacherId, + addedDate = addedDate + ).also { + it.idString = longId + } data.announcementList.add(announcementObject) data.setSeenMetadataList.add(Metadata( @@ -54,8 +56,7 @@ class LibrusApiAnnouncements(override val data: DataLibrus, Metadata.TYPE_ANNOUNCEMENT, id, read, - profile.empty || read, - addedDate + profile.empty || read )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendanceTypes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendanceTypes.kt index d7cbc00c..c620d13d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendanceTypes.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendanceTypes.kt @@ -26,25 +26,39 @@ class LibrusApiAttendanceTypes(override val data: DataLibrus, attendanceTypes?.forEach { attendanceType -> val id = attendanceType.getLong("Id") ?: return@forEach - val name = attendanceType.getString("Name") ?: "" - val color = attendanceType.getString("ColorRGB")?.let { Color.parseColor("#$it") } ?: -1 - val standardId = when (attendanceType.getBoolean("Standard") ?: false) { - true -> id - false -> attendanceType.getJsonObject("StandardType")?.getLong("Id") ?: id - } - val type = when (standardId) { + val typeName = attendanceType.getString("Name") ?: "" + val typeSymbol = attendanceType.getString("Short") ?: "" + val typeColor = attendanceType.getString("ColorRGB")?.let { Color.parseColor("#$it") } + + val isStandard = attendanceType.getBoolean("Standard") ?: false + val baseType = when (attendanceType.getJsonObject("StandardType")?.getLong("Id") ?: id) { 1L -> Attendance.TYPE_ABSENT 2L -> Attendance.TYPE_BELATED 3L -> Attendance.TYPE_ABSENT_EXCUSED 4L -> Attendance.TYPE_RELEASED - /*100*/else -> Attendance.TYPE_PRESENT + /*100*/else -> when (isStandard) { + true -> Attendance.TYPE_PRESENT + false -> Attendance.TYPE_PRESENT_CUSTOM + } + } + val typeShort = when (isStandard) { + true -> data.app.attendanceManager.getTypeShort(baseType) + false -> typeSymbol } - data.attendanceTypes.put(id, AttendanceType(profileId, id, name, type, color)) + data.attendanceTypes.put(id, AttendanceType( + profileId, + id, + baseType, + typeName, + typeShort, + typeSymbol, + typeColor + )) } - data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES, 4*DAY) + data.setSyncNext(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES, 2*DAY) onSuccess(ENDPOINT_LIBRUS_API_ATTENDANCE_TYPES) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendances.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendances.kt index 3727788d..7857dc2a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendances.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiAttendances.kt @@ -13,7 +13,6 @@ import pl.szczodrzynski.edziennik.data.db.entity.Attendance import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.utils.models.Date -import pl.szczodrzynski.edziennik.utils.models.Time class LibrusApiAttendances(override val data: DataLibrus, override val lastSync: Long?, @@ -42,9 +41,9 @@ class LibrusApiAttendances(override val data: DataLibrus, val lessonDate = Date.fromY_m_d(attendance.getString("Date")) val teacherId = attendance.getJsonObject("AddedBy")?.getLong("Id") val semester = attendance.getInt("Semester") ?: return@forEach - val type = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach - val typeObject = data.attendanceTypes[type] ?: null - val topic = typeObject?.name ?: "" + + val typeId = attendance.getJsonObject("Type")?.getLong("Id") ?: return@forEach + val type = data.attendanceTypes[typeId] ?: null val startTime = data.lessonRanges.get(lessonNo)?.startTime @@ -52,29 +51,34 @@ class LibrusApiAttendances(override val data: DataLibrus, data.librusLessons.singleOrNull { it.lessonId == lessonId } else null - val attendanceObject = Attendance( - profileId, - id, - teacherId ?: lesson?.teacherId ?: -1, - lesson?.subjectId ?: -1, - semester, - topic, - lessonDate, - startTime ?: Time(0, 0, 0), - typeObject?.type ?: Attendance.TYPE_CUSTOM - ) - val addedDate = Date.fromIso(attendance.getString("AddDate") ?: return@forEach) + val attendanceObject = Attendance( + profileId = profileId, + id = id, + baseType = type?.baseType ?: Attendance.TYPE_UNKNOWN, + typeName = type?.typeName ?: "nieznany rodzaj", + typeShort = type?.typeShort ?: "?", + typeSymbol = type?.typeSymbol ?: "?", + typeColor = type?.typeColor, + date = lessonDate, + startTime = startTime, + semester = semester, + teacherId = teacherId ?: lesson?.teacherId ?: -1, + subjectId = lesson?.subjectId ?: -1, + addedDate = addedDate + ).also { + it.lessonNumber = lessonNo + } + data.attendanceList.add(attendanceObject) - if(typeObject?.type != Attendance.TYPE_PRESENT) { + if(type?.baseType != Attendance.TYPE_PRESENT) { data.metadataList.add(Metadata( profileId, Metadata.TYPE_ATTENDANCE, id, - profile?.empty ?: false, - profile?.empty ?: false, - addedDate + profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN, + profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiBehaviourGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiBehaviourGrades.kt index 5a374ec9..fae79a50 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiBehaviourGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiBehaviourGrades.kt @@ -55,7 +55,8 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus, comment = null, semester = 1, teacherId = -1, - subjectId = 1 + subjectId = 1, + addedDate = profile.getSemesterStart(1).inMillis ) data.gradeList.add(semester1StartGradeObject) @@ -64,8 +65,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus, Metadata.TYPE_GRADE, semester1StartGradeObject.id, true, - true, - profile.getSemesterStart(1).inMillis + true )) } @@ -83,7 +83,8 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus, comment = null, semester = 2, teacherId = -1, - subjectId = 1 + subjectId = 1, + addedDate = profile.getSemesterStart(2).inMillis ) data.gradeList.add(semester2StartGradeObject) @@ -92,8 +93,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus, Metadata.TYPE_GRADE, semester2StartGradeObject.id, true, - true, - profile.getSemesterStart(2).inMillis + true )) } @@ -155,7 +155,8 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus, comment = if (text != null) description.join(" - ") else null, semester = semester, teacherId = teacherId, - subjectId = 1 + subjectId = 1, + addedDate = addedDate ).apply { valueMax = valueTo } @@ -166,8 +167,7 @@ class LibrusApiBehaviourGrades(override val data: DataLibrus, Metadata.TYPE_GRADE, id, profile.empty, - profile.empty, - addedDate + profile.empty )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiDescriptiveGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiDescriptiveGrades.kt index 42e318e0..8cc4f1c1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiDescriptiveGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiDescriptiveGrades.kt @@ -65,7 +65,8 @@ class LibrusApiDescriptiveGrades(override val data: DataLibrus, comment = null, semester = semester, teacherId = teacherId, - subjectId = subjectId + subjectId = subjectId, + addedDate = addedDate ) data.gradeList.add(gradeObject) @@ -74,8 +75,7 @@ class LibrusApiDescriptiveGrades(override val data: DataLibrus, Metadata.TYPE_GRADE, id, profile.empty, - profile.empty, - addedDate + profile.empty )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiEvents.kt index 52f52844..7a76f88e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiEvents.kt @@ -35,7 +35,7 @@ class LibrusApiEvents(override val data: DataLibrus, events?.forEach { event -> val id = event.getLong("Id") ?: return@forEach val eventDate = Date.fromY_m_d(event.getString("Date")) - val topic = event.getString("Content")?.trim() ?: "" + var topic = event.getString("Content")?.trim() ?: "" val type = event.getJsonObject("Category")?.getLong("Id") ?: -1 val teacherId = event.getJsonObject("CreatedBy")?.getLong("Id") ?: -1 val subjectId = event.getJsonObject("Subject")?.getLong("Id") ?: -1 @@ -46,6 +46,12 @@ class LibrusApiEvents(override val data: DataLibrus, val startTime = lessonRange?.startTime ?: Time.fromH_m(event.getString("TimeFrom")) val addedDate = Date.fromIso(event.getString("AddDate")) + event.getJsonObject("onlineLessonUrl")?.let { onlineLesson -> + val text = onlineLesson.getString("text")?.let { "$it - " } ?: "" + val url = onlineLesson.getString("url") + topic += "\n\n$text$url" + } + val eventObject = Event( profileId = profileId, id = id, @@ -56,7 +62,8 @@ class LibrusApiEvents(override val data: DataLibrus, type = type, teacherId = teacherId, subjectId = subjectId, - teamId = teamId + teamId = teamId, + addedDate = addedDate ) data.eventList.add(eventObject) @@ -66,8 +73,7 @@ class LibrusApiEvents(override val data: DataLibrus, Metadata.TYPE_EVENT, id, profile?.empty ?: false, - profile?.empty ?: false, - addedDate + profile?.empty ?: false )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiGrades.kt index 7ff42fde..c2e2d0ce 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiGrades.kt @@ -79,7 +79,8 @@ class LibrusApiGrades(override val data: DataLibrus, comment = null, semester = semester, teacherId = teacherId, - subjectId = subjectId + subjectId = subjectId, + addedDate = addedDate ) grade.getJsonObject("Improvement")?.also { @@ -98,8 +99,7 @@ class LibrusApiGrades(override val data: DataLibrus, Metadata.TYPE_GRADE, id, profile.empty, - profile.empty, - addedDate + profile.empty )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiHomework.kt index d47fbfdf..c1183c19 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiHomework.kt @@ -43,7 +43,8 @@ class LibrusApiHomework(override val data: DataLibrus, type = -1, teacherId = teacherId, subjectId = -1, - teamId = -1 + teamId = -1, + addedDate = addedDate.inMillis ) data.eventList.add(eventObject) @@ -52,8 +53,7 @@ class LibrusApiHomework(override val data: DataLibrus, Metadata.TYPE_HOMEWORK, id, profile?.empty ?: false, - profile?.empty ?: false, - addedDate.inMillis + profile?.empty ?: false )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiLuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiLuckyNumber.kt index 5a52cd69..80e6e299 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiLuckyNumber.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiLuckyNumber.kt @@ -33,9 +33,9 @@ class LibrusApiLuckyNumber(override val data: DataLibrus, val luckyNumberDate = Date.fromY_m_d(luckyNumberEl.getString("LuckyNumberDay")) ?: Date.getToday() val luckyNumber = luckyNumberEl.getInt("LuckyNumber") ?: -1 val luckyNumberObject = LuckyNumber( - profileId, - luckyNumberDate, - luckyNumber + profileId = profileId, + date = luckyNumberDate, + number = luckyNumber ) if (luckyNumberDate >= Date.getToday()) @@ -50,8 +50,7 @@ class LibrusApiLuckyNumber(override val data: DataLibrus, Metadata.TYPE_LUCKY_NUMBER, luckyNumberObject.date.value.toLong(), true, - profile?.empty ?: false, - System.currentTimeMillis() + profile?.empty ?: false )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiNotices.kt index e05cbfd7..ba531afe 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiNotices.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiNotices.kt @@ -46,12 +46,15 @@ class LibrusApiNotices(override val data: DataLibrus, val semester = profile?.dateToSemester(addedDate) ?: 1 val noticeObject = Notice( - profileId, - id, - categoryText + "\n" + text, - semester, - type, - teacherId + profileId = profileId, + id = id, + type = type, + semester = semester, + text = text, + category = categoryText, + points = null, + teacherId = teacherId, + addedDate = addedDate.inMillis ) data.noticeList.add(noticeObject) @@ -61,8 +64,7 @@ class LibrusApiNotices(override val data: DataLibrus, Metadata.TYPE_NOTICE, id, profile?.empty ?: false, - profile?.empty ?: false, - addedDate.inMillis + profile?.empty ?: false )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPointGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPointGrades.kt index fae45318..9da2c435 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPointGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPointGrades.kt @@ -56,7 +56,8 @@ class LibrusApiPointGrades(override val data: DataLibrus, comment = null, semester = semester, teacherId = teacherId, - subjectId = subjectId + subjectId = subjectId, + addedDate = addedDate ).apply { valueMax = category?.valueTo ?: 0f } @@ -67,8 +68,7 @@ class LibrusApiPointGrades(override val data: DataLibrus, Metadata.TYPE_GRADE, id, profile.empty, - profile.empty, - addedDate + profile.empty )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPtMeetings.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPtMeetings.kt index de696a99..6c0a9dd7 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPtMeetings.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiPtMeetings.kt @@ -58,8 +58,7 @@ class LibrusApiPtMeetings(override val data: DataLibrus, Metadata.TYPE_EVENT, id, profile?.empty ?: false, - profile?.empty ?: false, - System.currentTimeMillis() + profile?.empty ?: false )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTeacherFreeDays.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTeacherFreeDays.kt index 3d4db1aa..973ada46 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTeacherFreeDays.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTeacherFreeDays.kt @@ -43,15 +43,15 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus, val timeTo = teacherAbsence.getString("TimeTo")?.let { Time.fromH_m_s(it) } val teacherAbsenceObject = TeacherAbsence( - profileId, - id, - teacherId, - type, - name, - dateFrom, - dateTo, - timeFrom, - timeTo + profileId = profileId, + id = id, + type = type, + name = name, + dateFrom = dateFrom, + dateTo = dateTo, + timeFrom = timeFrom, + timeTo = timeTo, + teacherId = teacherId ) data.teacherAbsenceList.add(teacherAbsenceObject) @@ -60,8 +60,7 @@ class LibrusApiTeacherFreeDays(override val data: DataLibrus, Metadata.TYPE_TEACHER_ABSENCE, id, true, - profile?.empty ?: false, - System.currentTimeMillis() + profile?.empty ?: false )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTextGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTextGrades.kt index cf020425..7b7c8c6f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTextGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTextGrades.kt @@ -60,7 +60,8 @@ class LibrusApiTextGrades(override val data: DataLibrus, comment = grade.getString("Phrase") /* whatever it is */, semester = semester, teacherId = teacherId, - subjectId = subjectId + subjectId = subjectId, + addedDate = addedDate ) data.gradeList.add(gradeObject) @@ -69,8 +70,7 @@ class LibrusApiTextGrades(override val data: DataLibrus, Metadata.TYPE_GRADE, id, profile.empty, - profile.empty, - addedDate + profile.empty )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTimetables.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTimetables.kt index 7c1eec61..9a463a6b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTimetables.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/api/LibrusApiTimetables.kt @@ -198,8 +198,7 @@ class LibrusApiTimetables(override val data: DataLibrus, Metadata.TYPE_LESSON_CHANGE, lessonObject.id, seen, - seen, - System.currentTimeMillis() + seen )) } data.lessonList.add(lessonObject) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetList.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetList.kt index 35148cef..9f962bf9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetList.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetList.kt @@ -97,7 +97,8 @@ class LibrusMessagesGetList(override val data: DataLibrus, type = type, subject = subject, body = null, - senderId = senderId + senderId = senderId, + addedDate = sentDate ) val messageRecipientObject = MessageRecipient( @@ -120,8 +121,7 @@ class LibrusMessagesGetList(override val data: DataLibrus, Metadata.TYPE_MESSAGE, id, notified, - notified, - sentDate + notified )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetMessage.kt index bb4c620b..49ee0d52 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetMessage.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetMessage.kt @@ -150,8 +150,7 @@ class LibrusMessagesGetMessage(override val data: DataLibrus, Metadata.TYPE_MESSAGE, messageObject.id, true, - true, - messageObject.addedDate + true )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesSendMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesSendMessage.kt index 9364f2b8..d568135e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesSendMessage.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesSendMessage.kt @@ -50,7 +50,7 @@ class LibrusMessagesSendMessage(override val data: DataLibrus, LibrusMessagesGetList(data, type = Message.TYPE_SENT, lastSync = null) { val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.id == id } val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id } - val event = MessageSentEvent(data.profileId, message, metadata?.addedDate) + val event = MessageSentEvent(data.profileId, message, message?.addedDate) EventBus.getDefault().postSticky(event) onSuccess() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetAttachment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetAttachment.kt new file mode 100644 index 00000000..6b1f2eba --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetAttachment.kt @@ -0,0 +1,24 @@ +package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia + +import pl.szczodrzynski.edziennik.data.api.LIBRUS_SYNERGIA_MESSAGES_ATTACHMENT_URL +import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus +import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusSynergia +import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.messages.LibrusSandboxDownloadAttachment +import pl.szczodrzynski.edziennik.data.db.entity.Message + +class LibrusSynergiaGetAttachment(override val data: DataLibrus, + val message: Message, + val attachmentId: Long, + val attachmentName: String, + val onSuccess: () -> Unit +) : LibrusSynergia(data, null) { + companion object { + const val TAG = "LibrusSynergiaGetAttachment" + } + + init { + redirectUrlGet(TAG, "$LIBRUS_SYNERGIA_MESSAGES_ATTACHMENT_URL/${message.id}/$attachmentId") { url -> + LibrusSandboxDownloadAttachment(data, url, message, attachmentId, attachmentName, onSuccess) + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessage.kt new file mode 100644 index 00000000..4cb1e246 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessage.kt @@ -0,0 +1,160 @@ +package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia + +import org.greenrobot.eventbus.EventBus +import org.jsoup.Jsoup +import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus +import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusSynergia +import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent +import pl.szczodrzynski.edziennik.data.db.entity.Message +import pl.szczodrzynski.edziennik.data.db.entity.Metadata +import pl.szczodrzynski.edziennik.data.db.entity.Teacher +import pl.szczodrzynski.edziennik.data.db.full.MessageFull +import pl.szczodrzynski.edziennik.data.db.full.MessageRecipientFull +import pl.szczodrzynski.edziennik.get +import pl.szczodrzynski.edziennik.isNotNullNorEmpty +import pl.szczodrzynski.edziennik.singleOrNull +import pl.szczodrzynski.edziennik.swapFirstLastName +import pl.szczodrzynski.edziennik.utils.models.Date + +class LibrusSynergiaGetMessage(override val data: DataLibrus, + private val messageObject: MessageFull, + val onSuccess: () -> Unit) : LibrusSynergia(data, null) { + companion object { + const val TAG = "LibrusSynergiaGetMessage" + } + + init { + val endpoint = when (messageObject.type) { + Message.TYPE_SENT -> "wiadomosci/1/6/${messageObject.id}/f0" + else -> "wiadomosci/1/5/${messageObject.id}/f0" + } + + data.profile?.also { profile -> + synergiaGet(TAG, endpoint) { text -> + val doc = Jsoup.parse(text) + + val messageElement = doc.select(".container-message tr")[0].child(1) + val detailsElement = messageElement.child(1) + val readElement = messageElement.children().last() + + val body = messageElement.select(".container-message-content").html() + + messageObject.apply { + this.body = body + + clearAttachments() + if (messageElement.children().size >= 5) { + messageElement.child(3).select("tr").forEachIndexed { i, attachment -> + if (i == 0) return@forEachIndexed // Skip the header + val filename = attachment.child(0).text().trim() + val attachmentId = "wiadomosci\\\\/pobierz_zalacznik\\\\/[0-9]+?\\\\/([0-9]+)\"".toRegex() + .find(attachment.select("img").attr("onclick"))?.get(1) + ?: return@forEachIndexed + addAttachment(attachmentId.toLong(), filename, -1) + } + } + } + + val messageRecipientList = mutableListOf() + + when (messageObject.type) { + Message.TYPE_RECEIVED -> { + val senderFullName = detailsElement.child(0).select(".left").text() + val senderGroupName = "\\[(.+?)]".toRegex().find(senderFullName)?.get(1)?.trim() + + data.teacherList.singleOrNull { it.id == messageObject.senderId }?.apply { + setTeacherType(when (senderGroupName) { + /* https://api.librus.pl/2.0/Messages/Role */ + "Pomoc techniczna Librus", "SuperAdministrator" -> Teacher.TYPE_SUPER_ADMIN + "Administrator szkoły" -> Teacher.TYPE_SCHOOL_ADMIN + "Dyrektor Szkoły" -> Teacher.TYPE_PRINCIPAL + "Nauczyciel" -> Teacher.TYPE_TEACHER + "Rodzic", "Opiekun" -> Teacher.TYPE_PARENT + "Sekretariat" -> Teacher.TYPE_SECRETARIAT + "Uczeń" -> Teacher.TYPE_STUDENT + "Pedagog/Psycholog szkolny" -> Teacher.TYPE_PEDAGOGUE + "Pracownik biblioteki" -> Teacher.TYPE_LIBRARIAN + "Inny specjalista" -> Teacher.TYPE_SPECIALIST + "Jednostka Nadrzędna" -> { + typeDescription = "Jednostka Nadrzędna" + Teacher.TYPE_OTHER + } + "Jednostka Samorządu Terytorialnego" -> { + typeDescription = "Jednostka Samorządu Terytorialnego" + Teacher.TYPE_OTHER + } + else -> Teacher.TYPE_OTHER + }) + } + + val readDateText = readElement.select(".left").text() + val readDate = when (readDateText.isNotNullNorEmpty()) { + true -> Date.fromIso(readDateText) + else -> 0 + } + + val messageRecipientObject = MessageRecipientFull( + profileId = profileId, + id = -1, + messageId = messageObject.id, + readDate = readDate + ) + + messageRecipientObject.fullName = profile.accountName + ?: profile.studentNameLong + + messageRecipientList.add(messageRecipientObject) + } + + Message.TYPE_SENT -> { + + readElement.select("tr").forEachIndexed { i, receiver -> + if (i == 0) return@forEachIndexed // Skip the header + + val receiverFullName = receiver.child(0).text() + val receiverName = receiverFullName.split('(')[0].swapFirstLastName() + + val teacher = data.teacherList.singleOrNull { it.fullName == receiverName } + val receiverId = teacher?.id ?: -1 + + val readDate = when (val readDateText = receiver.child(1).text().trim()) { + "NIE" -> 0 + else -> Date.fromIso(readDateText) + } + + val messageRecipientObject = MessageRecipientFull( + profileId = profileId, + id = receiverId, + messageId = messageObject.id, + readDate = readDate + ) + + messageRecipientObject.fullName = receiverName + + messageRecipientList.add(messageRecipientObject) + } + } + } + + if (!messageObject.seen) { + data.setSeenMetadataList.add(Metadata( + messageObject.profileId, + Metadata.TYPE_MESSAGE, + messageObject.id, + true, + true + )) + } + + messageObject.recipients = messageRecipientList + data.messageRecipientList.addAll(messageRecipientList) + + data.messageList.add(messageObject) + data.messageListReplace = true + + EventBus.getDefault().postSticky(MessageGetEvent(messageObject)) + onSuccess() + } + } ?: onSuccess() + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessages.kt new file mode 100644 index 00000000..ffc8133a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaGetMessages.kt @@ -0,0 +1,116 @@ +package pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.synergia + +import org.jsoup.Jsoup +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.data.api.ERROR_NOT_IMPLEMENTED +import pl.szczodrzynski.edziennik.data.api.Regexes +import pl.szczodrzynski.edziennik.data.api.edziennik.librus.* +import pl.szczodrzynski.edziennik.data.api.edziennik.librus.data.LibrusSynergia +import pl.szczodrzynski.edziennik.data.db.entity.* +import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.models.Date + +class LibrusSynergiaGetMessages(override val data: DataLibrus, + override val lastSync: Long?, + private val type: Int = Message.TYPE_RECEIVED, + archived: Boolean = false, + val onSuccess: (Int) -> Unit) : LibrusSynergia(data, lastSync) { + companion object { + const val TAG = "LibrusSynergiaGetMessages" + } + + init { + val endpoint = when (type) { + Message.TYPE_RECEIVED -> "wiadomosci/5" + Message.TYPE_SENT -> "wiadomosci/6" + else -> null + } + val endpointId = when (type) { + Message.TYPE_RECEIVED -> ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_RECEIVED + else -> ENDPOINT_LIBRUS_SYNERGIA_MESSAGES_SENT + } + + if (endpoint != null) { + synergiaGet(TAG, endpoint) { text -> + val doc = Jsoup.parse(text) + + fun getRecipientId(name: String): Long = data.teacherList.singleOrNull { + it.fullNameLastFirst == name + }?.id ?: { + val teacherObject = Teacher( + profileId, + -1 * Utils.crc16(name.swapFirstLastName().toByteArray()).toLong(), + name.splitName()?.second!!, + name.splitName()?.first!! + ) + data.teacherList.put(teacherObject.id, teacherObject) + teacherObject.id + }.invoke() + + doc.select(".decorated.stretch tbody > tr").forEach { messageElement -> + val url = messageElement.select("a").first().attr("href") + val id = Regexes.LIBRUS_MESSAGE_ID.find(url)?.get(1)?.toLong() ?: return@forEach + val subject = messageElement.child(3).text() + val sentDate = Date.fromIso(messageElement.child(4).text()) + val recipientName = messageElement.child(2).text().split('(')[0].fixName() + val recipientId = getRecipientId(recipientName) + val read = messageElement.child(2).attr("style").isNullOrBlank() + + val senderId = when (type) { + Message.TYPE_RECEIVED -> recipientId + else -> null + } + + val receiverId = when (type) { + Message.TYPE_RECEIVED -> -1 + else -> recipientId + } + + val notified = when (type) { + Message.TYPE_SENT -> true + else -> read || profile?.empty ?: false + } + + val messageObject = Message( + profileId = profileId, + id = id, + type = type, + subject = subject, + body = null, + senderId = senderId, + addedDate = sentDate + ) + + val messageRecipientObject = MessageRecipient( + profileId, + receiverId, + -1, + if (read) 1 else 0, + id + ) + + messageObject.hasAttachments = !messageElement.child(1).select("img").isEmpty() + + data.messageList.add(messageObject) + data.messageRecipientList.add(messageRecipientObject) + data.setSeenMetadataList.add(Metadata( + profileId, + Metadata.TYPE_MESSAGE, + id, + notified, + notified + )) + } + + when (type) { + Message.TYPE_RECEIVED -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_RECEIVED, SYNC_ALWAYS) + Message.TYPE_SENT -> data.setSyncNext(ENDPOINT_LIBRUS_MESSAGES_SENT, DAY, MainActivity.DRAWER_ITEM_MESSAGES) + } + onSuccess(endpointId) + } + } else { + data.error(TAG, ERROR_NOT_IMPLEMENTED) + onSuccess(endpointId) + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaHomework.kt index 48f88ef4..d33f6911 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/synergia/LibrusSynergiaHomework.kt @@ -58,7 +58,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus, elements[9].select("input").attr("onclick") )?.get(1)?.toLong() ?: return@forEachIndexed - val lessons = data.db.timetableDao().getForDateNow(profileId, eventDate) + val lessons = data.db.timetableDao().getAllForDateNow(profileId, eventDate) val startTime = lessons.firstOrNull { it.subjectId == subjectId }?.startTime val seen = when (profile.empty) { @@ -76,7 +76,8 @@ class LibrusSynergiaHomework(override val data: DataLibrus, type = Event.TYPE_HOMEWORK, teacherId = teacherId, subjectId = subjectId, - teamId = data.teamClass?.id ?: -1 + teamId = data.teamClass?.id ?: -1, + addedDate = addedDate.inMillis ) data.eventList.add(eventObject) @@ -85,8 +86,7 @@ class LibrusSynergiaHomework(override val data: DataLibrus, Metadata.TYPE_HOMEWORK, id, seen, - seen, - addedDate.inMillis + seen )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginApi.kt index fe155abe..7151cf29 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginApi.kt @@ -18,6 +18,7 @@ import pl.szczodrzynski.edziennik.getUnixDate import pl.szczodrzynski.edziennik.utils.Utils.d import java.net.HttpURLConnection.* +@Suppress("ConvertSecondaryConstructorToPrimary") class LibrusLoginApi { companion object { private const val TAG = "LoginLibrusApi" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginMessages.kt index ba8bcf76..4a994472 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginMessages.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/login/LibrusLoginMessages.kt @@ -10,6 +10,7 @@ import im.wangchao.mhttp.body.MediaTypeUtils import im.wangchao.mhttp.callback.TextCallbackHandler import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.edziennik.librus.DataLibrus +import pl.szczodrzynski.edziennik.data.api.edziennik.librus.LibrusRecaptchaHelper import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.getUnixDate import pl.szczodrzynski.edziennik.utils.Utils.d @@ -35,17 +36,35 @@ class LibrusLoginMessages(val data: DataLibrus, val onSuccess: () -> Unit) { onSuccess() } + text?.contains("grecaptcha.ready") == true -> { + val url = response?.request()?.url()?.toString() ?: run { + data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text) + return + } + + LibrusRecaptchaHelper(data.app, url, text, onSuccess = { newUrl -> + loginWithSynergia(newUrl) + }, onTimeout = { + data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_TIMEOUT, response, text) + }) + } + text?.contains("ok") == true -> { saveSessionId(response, text) onSuccess() } text?.contains("Niepoprawny login i/lub hasło.") == true -> data.error(TAG, ERROR_LOGIN_LIBRUS_MESSAGES_INVALID_LOGIN, response, text) text?.contains("stop.png") == true -> data.error(TAG, ERROR_LIBRUS_SYNERGIA_ACCESS_DENIED, response, text) - text?.contains("eAccessDeny") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text) + text?.contains("eAccessDeny") == true -> { + // data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text) + data.messagesLoginSuccessful = false + onSuccess() + } text?.contains("OffLine") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_MAINTENANCE, response, text) text?.contains("error") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ERROR, response, text) text?.contains("eVarWhitThisNameNotExists") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_ACCESS_DENIED, response, text) text?.contains("") == true -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text) + else -> data.error(TAG, ERROR_LIBRUS_MESSAGES_OTHER, response, text) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiAttendance.kt index be68de4d..473b3691 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiAttendance.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiAttendance.kt @@ -6,7 +6,10 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.api import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.db.entity.Attendance -import pl.szczodrzynski.edziennik.data.db.entity.Attendance.* +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT_EXCUSED +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_RELEASED import pl.szczodrzynski.edziennik.data.db.entity.Metadata class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List) { @@ -23,7 +26,7 @@ class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List) val id = cols[0].toLong() val lessonId = cols[1].toLong() data.mobiLessons.singleOrNull { it.id == lessonId }?.let { lesson -> - val type = when (cols[4]) { + val baseType = when (cols[4]) { "2" -> TYPE_ABSENT "5" -> TYPE_ABSENT_EXCUSED "4" -> TYPE_RELEASED @@ -31,16 +34,37 @@ class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List) } val semester = data.profile?.dateToSemester(lesson.date) ?: 1 + val typeName = when (baseType) { + TYPE_ABSENT -> "nieobecność" + TYPE_ABSENT_EXCUSED -> "nieobecność usprawiedliwiona" + TYPE_RELEASED -> "zwolnienie" + TYPE_PRESENT -> "obecność" + else -> "nieznany rodzaj" + } + val typeSymbol = when (baseType) { + TYPE_ABSENT -> "|" + TYPE_ABSENT_EXCUSED -> "+" + TYPE_RELEASED -> "z" + TYPE_PRESENT -> "." + else -> "?" + } + val attendanceObject = Attendance( - data.profileId, - id, - lesson.teacherId, - lesson.subjectId, - semester, - lesson.topic, - lesson.date, - lesson.startTime, - type) + profileId = data.profileId, + id = id, + baseType = baseType, + typeName = typeName, + typeShort = data.app.attendanceManager.getTypeShort(baseType), + typeSymbol = typeSymbol, + typeColor = null, + date = lesson.date, + startTime = lesson.startTime, + semester = semester, + teacherId = lesson.teacherId, + subjectId = lesson.subjectId + ).also { + it.lessonTopic = lesson.topic + } data.attendanceList.add(attendanceObject) data.metadataList.add( @@ -48,9 +72,8 @@ class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List) data.profileId, Metadata.TYPE_ATTENDANCE, id, - data.profile?.empty ?: false, - data.profile?.empty ?: false, - System.currentTimeMillis() + data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN, + data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiEvents.kt index 02fbb67e..7a72d3de 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiEvents.kt @@ -60,7 +60,9 @@ class MobidziennikApiEvents(val data: DataMobidziennik, rows: List) { type = type, teacherId = teacherId, subjectId = subjectId, - teamId = teamId) + teamId = teamId, + addedDate = addedDate + ) data.eventList.add(eventObject) data.metadataList.add( @@ -69,8 +71,7 @@ class MobidziennikApiEvents(val data: DataMobidziennik, rows: List) { Metadata.TYPE_EVENT, id, data.profile?.empty ?: false, - data.profile?.empty ?: false, - addedDate + data.profile?.empty ?: false )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiGrades.kt index 609c895c..c3c1b449 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiGrades.kt @@ -79,7 +79,9 @@ class MobidziennikApiGrades(val data: DataMobidziennik, rows: List) { comment = null, semester = semester, teacherId = teacherId, - subjectId = subjectId) + subjectId = subjectId, + addedDate = addedDate + ) if (data.profile?.empty == true) { addedDate = data.profile.dateSemester1Start.inMillis @@ -92,8 +94,7 @@ class MobidziennikApiGrades(val data: DataMobidziennik, rows: List) { Metadata.TYPE_GRADE, id, data.profile?.empty ?: false, - data.profile?.empty ?: false, - addedDate + data.profile?.empty ?: false )) addedDate++ } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt index 4623a26d..683a8820 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiHomework.kt @@ -40,7 +40,8 @@ class MobidziennikApiHomework(val data: DataMobidziennik, rows: List) { type = Event.TYPE_HOMEWORK, teacherId = teacherId, subjectId = subjectId, - teamId = teamId) + teamId = teamId + ) data.eventList.add(eventObject) data.metadataList.add( @@ -49,8 +50,7 @@ class MobidziennikApiHomework(val data: DataMobidziennik, rows: List) { Metadata.TYPE_HOMEWORK, id, data.profile?.empty ?: false, - data.profile?.empty ?: false, - System.currentTimeMillis() + data.profile?.empty ?: false )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiNotices.kt index f1d9aed2..66bfe1fd 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiNotices.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiNotices.kt @@ -33,12 +33,16 @@ class MobidziennikApiNotices(val data: DataMobidziennik, rows: List) { val addedDate = Date.fromYmd(cols[7]).inMillis val noticeObject = Notice( - data.profileId, - id, - text, - semester, - type, - teacherId) + profileId = data.profileId, + id = id, + type = type, + semester = semester, + text = text, + category = null, + points = null, + teacherId = teacherId, + addedDate = addedDate + ) data.noticeList.add(noticeObject) data.metadataList.add( @@ -47,8 +51,7 @@ class MobidziennikApiNotices(val data: DataMobidziennik, rows: List) { Metadata.TYPE_NOTICE, id, data.profile?.empty ?: false, - data.profile?.empty ?: false, - addedDate + data.profile?.empty ?: false )) } }} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiTimetable.kt index 6a7ce02d..a82ab4ef 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiTimetable.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/api/MobidziennikApiTimetable.kt @@ -8,9 +8,9 @@ import android.util.SparseArray import androidx.core.util.set import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel +import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.data.db.entity.LessonRange import pl.szczodrzynski.edziennik.data.db.entity.Metadata -import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.fixName import pl.szczodrzynski.edziennik.keys import pl.szczodrzynski.edziennik.singleOrNull @@ -97,8 +97,7 @@ class MobidziennikApiTimetable(val data: DataMobidziennik, rows: List) { Metadata.TYPE_LESSON_CHANGE, it.id, seen, - seen, - System.currentTimeMillis() + seen )) } data.lessonList += it diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt index 5e1445ec..ece23d7f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikLuckyNumberExtractor.kt @@ -17,9 +17,9 @@ class MobidziennikLuckyNumberExtractor(val data: DataMobidziennik, text: String) val luckyNumber = it.groupValues[1].toInt() val luckyNumberObject = LuckyNumber( - data.profileId, - Date.getToday(), - luckyNumber + profileId = data.profileId, + date = Date.getToday(), + number = luckyNumber ) data.luckyNumberList.add(luckyNumberObject) @@ -29,8 +29,7 @@ class MobidziennikLuckyNumberExtractor(val data: DataMobidziennik, text: String) Metadata.TYPE_LUCKY_NUMBER, luckyNumberObject.date.value.toLong(), true, - data.profile?.empty ?: false, - System.currentTimeMillis() + data.profile?.empty ?: false )) } catch (_: Exception){} } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt index c56c56d9..36dbe1e9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt @@ -11,7 +11,13 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.ENDPOINT_MOBID import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.db.entity.Attendance -import pl.szczodrzynski.edziennik.data.db.entity.Attendance.* +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_ABSENT_EXCUSED +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_BELATED +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT_CUSTOM +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_RELEASED +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_UNKNOWN import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.fixName @@ -71,6 +77,18 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik, val start = System.currentTimeMillis() + val types = Regexes.MOBIDZIENNIK_ATTENDANCE_TYPES + .find(text) + ?.get(1) + ?.split("
") + ?.map { + it.trimEnd(',') + .split(" ", limit = 2) + .let { it.getOrNull(0) to it.getOrNull(1) } + } + ?.toMap() + val typeSymbols = types?.keys?.filterNotNull() ?: listOf() + Regexes.MOBIDZIENNIK_ATTENDANCE_TABLE.findAll(text).forEach { tableResult -> val table = tableResult[1] val lessonDates = mutableListOf() @@ -92,55 +110,90 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik, return@forEach ranges.forEach { range -> val lessonDate = dateIterator.next() - val entry = entriesIterator.next() + var entry = entriesIterator.next() if (entry.isBlank()) return@forEach val startTime = Time.fromH_m(range[1]) - val entryIterator = entry.iterator() + range[2].split(" / ").mapNotNull { Regexes.MOBIDZIENNIK_ATTENDANCE_LESSON.find(it) }.forEachIndexed { index, lesson -> - val topic = lesson[2] - if (topic.startsWith("Lekcja odwołana: ") || !entryIterator.hasNext()) + val topic = lesson[1].substringAfter(" - ", missingDelimiterValue = "").takeIf { it.isNotBlank() } + if (topic?.startsWith("Lekcja odwołana: ") == true || entry.isEmpty()) return@forEachIndexed - val subjectName = lesson[1] + val subjectName = lesson[1].substringBefore(" - ") //val team = lesson[3] - val teacherName = lesson[4].fixName() + val teacherName = lesson[3].fixName() val teacherId = data.teacherList.singleOrNull { it.fullNameLastFirst == teacherName }?.id ?: -1 val subjectId = data.subjectList.singleOrNull { it.longName == subjectName }?.id ?: -1 - val type = when (entryIterator.nextChar()) { - '.' -> TYPE_PRESENT - '|' -> TYPE_ABSENT - '+' -> TYPE_ABSENT_EXCUSED - 's' -> TYPE_BELATED - 'z' -> TYPE_RELEASED - else -> TYPE_PRESENT + var typeSymbol = "" + for (symbol in typeSymbols) { + if (entry.startsWith(symbol) && symbol.length > typeSymbol.length) + typeSymbol = symbol } + entry = entry.removePrefix(typeSymbol) + + var isCounted = true + val baseType = when (typeSymbol) { + "." -> TYPE_PRESENT + "|" -> TYPE_ABSENT + "+" -> TYPE_ABSENT_EXCUSED + "s" -> TYPE_BELATED + "z" -> TYPE_RELEASED + else -> { + isCounted = false + when (typeSymbol) { + "e" -> TYPE_PRESENT_CUSTOM + "en" -> TYPE_ABSENT + "ep" -> TYPE_PRESENT_CUSTOM + else -> TYPE_UNKNOWN + } + } + } + val typeName = types?.get(typeSymbol) ?: "" + val typeColor = when (typeSymbol) { + "e" -> 0xff673ab7 + "en" -> 0xffec407a + "ep" -> 0xff4caf50 + else -> null + }?.toInt() + + val typeShort = if (isCounted) + data.app.attendanceManager.getTypeShort(baseType) + else + typeSymbol + val semester = data.profile?.dateToSemester(lessonDate) ?: 1 val id = lessonDate.combineWith(startTime) / 6L * 10L + (lesson[0].hashCode() and 0xFFFF) + index val attendanceObject = Attendance( - data.profileId, - id, - teacherId, - subjectId, - semester, - topic, - lessonDate, - startTime, - type) + profileId = profileId, + id = id, + baseType = baseType, + typeName = typeName, + typeShort = typeShort, + typeSymbol = typeSymbol, + typeColor = typeColor, + date = lessonDate, + startTime = startTime, + semester = semester, + teacherId = teacherId, + subjectId = subjectId + ).also { + it.lessonTopic = topic + it.isCounted = isCounted + } data.attendanceList.add(attendanceObject) - if (type != TYPE_PRESENT) { + if (baseType != TYPE_PRESENT) { data.metadataList.add( Metadata( data.profileId, Metadata.TYPE_ATTENDANCE, id, - data.profile?.empty ?: false, - data.profile?.empty ?: false, - System.currentTimeMillis() + data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN, + data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebCalendar.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebCalendar.kt index c1749d76..95c1f6df 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebCalendar.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebCalendar.kt @@ -89,8 +89,8 @@ class MobidziennikWebCalendar(override val data: DataMobidziennik, Metadata.TYPE_EVENT, eventObject.id, profile?.empty ?: false, - profile?.empty ?: false, - System.currentTimeMillis() /* no addedDate here though */ + profile?.empty ?: false + /* no addedDate here though */ )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetMessage.kt index 1c44a219..105c0943 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetMessage.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetMessage.kt @@ -139,8 +139,7 @@ class MobidziennikWebGetMessage(override val data: DataMobidziennik, Metadata.TYPE_MESSAGE, message.id, true, - true, - message.addedDate + true )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGrades.kt index de058c91..9f5fab95 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGrades.kt @@ -125,7 +125,8 @@ class MobidziennikWebGrades(override val data: DataMobidziennik, comment = null, semester = gradeSemester, teacherId = teacherId, - subjectId = subjectId + subjectId = subjectId, + addedDate = gradeAddedDateMillis ) gradeObject.classAverage = gradeClassAverage @@ -137,8 +138,7 @@ class MobidziennikWebGrades(override val data: DataMobidziennik, Metadata.TYPE_GRADE, gradeObject.id, profile.empty, - profile.empty, - gradeAddedDateMillis + profile.empty )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesAll.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesAll.kt index c0373c54..4e8f900a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesAll.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesAll.kt @@ -77,11 +77,12 @@ class MobidziennikWebMessagesAll(override val data: DataMobidziennik, type = type, subject = subject, body = null, - senderId = senderId + senderId = senderId, + addedDate = addedDate ) data.messageList.add(message) - data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true, addedDate)) + data.metadataList.add(Metadata(profileId, Metadata.TYPE_MESSAGE, message.id, true, true)) } // sync every 7 days as we probably don't expect more than diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt index 385ea71e..be762228 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesInbox.kt @@ -63,7 +63,8 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik, type = Message.TYPE_RECEIVED, subject = subject, body = null, - senderId = senderId + senderId = senderId, + addedDate = addedDate ) if (hasAttachments) @@ -76,8 +77,7 @@ class MobidziennikWebMessagesInbox(override val data: DataMobidziennik, Metadata.TYPE_MESSAGE, message.id, isRead, - isRead || profile?.empty ?: false, - addedDate + isRead || profile?.empty ?: false )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesSent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesSent.kt index 918816c8..543df329 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesSent.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebMessagesSent.kt @@ -78,7 +78,8 @@ class MobidziennikWebMessagesSent(override val data: DataMobidziennik, type = Message.TYPE_SENT, subject = subject, body = null, - senderId = null + senderId = null, + addedDate = addedDate ) if (hasAttachments) @@ -91,8 +92,7 @@ class MobidziennikWebMessagesSent(override val data: DataMobidziennik, Metadata.TYPE_MESSAGE, message.id, true, - true, - addedDate + true )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebSendMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebSendMessage.kt index d0cee4dc..89178b8b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebSendMessage.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebSendMessage.kt @@ -45,7 +45,7 @@ class MobidziennikWebSendMessage(override val data: DataMobidziennik, MobidziennikWebMessagesAll(data, null) { val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject } val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == message?.id } - val event = MessageSentEvent(data.profileId, message, metadata?.addedDate) + val event = MessageSentEvent(data.profileId, message, message?.addedDate) EventBus.getDefault().postSticky(event) onSuccess() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt index 14adcbe3..4b397f5d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/DataVulcan.kt @@ -139,6 +139,15 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app get() { mStudentSemesterId = mStudentSemesterId ?: profile?.getStudentData("studentSemesterId", 0); return mStudentSemesterId ?: 0 } set(value) { profile?.putStudentData("studentSemesterId", value) ?: return; mStudentSemesterId = value } + private var mSemester1Id: Int? = null + var semester1Id: Int + get() { mSemester1Id = mSemester1Id ?: profile?.getStudentData("semester1Id", 0); return mSemester1Id ?: 0 } + set(value) { profile?.putStudentData("semester1Id", value) ?: return; mSemester1Id = value } + private var mSemester2Id: Int? = null + var semester2Id: Int + get() { mSemester2Id = mSemester2Id ?: profile?.getStudentData("semester2Id", 0); return mSemester2Id ?: 0 } + set(value) { profile?.putStudentData("semester2Id", value) ?: return; mSemester2Id = value } + /** * ListaUczniow/OkresNumer, e.g. 1 or 2 */ diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiAttendance.kt index 2ec240a4..2814a1cf 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiAttendance.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiAttendance.kt @@ -7,8 +7,9 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_ATTENDANCE import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi import pl.szczodrzynski.edziennik.data.db.entity.Attendance -import pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_PRESENT +import pl.szczodrzynski.edziennik.data.db.entity.Attendance.Companion.TYPE_PRESENT import pl.szczodrzynski.edziennik.data.db.entity.Metadata +import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.utils.models.Date @@ -25,57 +26,85 @@ class VulcanApiAttendance(override val data: DataVulcan, data.db.attendanceTypeDao().getAllNow(profileId).toSparseArray(data.attendanceTypes) { it.id } } - val startDate: String = profile.getSemesterStart(profile.currentSemester).stringY_m_d - val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d + val semesterId = data.studentSemesterId + val semesterNumber = data.studentSemesterNumber + if (semesterNumber == 2 && lastSync ?: 0 < profile.dateSemester1Start.inMillis) { + getAttendance(profile, semesterId - 1, semesterNumber - 1) { + getAttendance(profile, semesterId, semesterNumber) { + finish() + } + } + } + else { + getAttendance(profile, semesterId, semesterNumber) { + finish() + } + } + + } ?: onSuccess(ENDPOINT_VULCAN_API_ATTENDANCE) } + + private fun finish() { + data.setSyncNext(ENDPOINT_VULCAN_API_ATTENDANCE, SYNC_ALWAYS) + onSuccess(ENDPOINT_VULCAN_API_ATTENDANCE) + } + + private fun getAttendance(profile: Profile, semesterId: Int, semesterNumber: Int, onSuccess: () -> Unit) { + val startDate = profile.getSemesterStart(semesterNumber).stringY_m_d + val endDate = profile.getSemesterEnd(semesterNumber).stringY_m_d apiGet(TAG, VULCAN_API_ENDPOINT_ATTENDANCE, parameters = mapOf( "DataPoczatkowa" to startDate, "DataKoncowa" to endDate, "IdOddzial" to data.studentClassId, "IdUczen" to data.studentId, - "IdOkresKlasyfikacyjny" to data.studentSemesterId + "IdOkresKlasyfikacyjny" to semesterId )) { json, _ -> json.getJsonObject("Data")?.getJsonArray("Frekwencje")?.forEach { attendanceEl -> val attendance = attendanceEl.asJsonObject - val attendanceCategory = data.attendanceTypes.get(attendance.getLong("IdKategoria") ?: return@forEach) + val type = data.attendanceTypes.get(attendance.getLong("IdKategoria") ?: return@forEach) ?: return@forEach - val type = attendanceCategory.type - val id = (attendance.getInt("Dzien") ?: 0) + (attendance.getInt("Numer") ?: 0) val lessonDateMillis = Date.fromY_m_d(attendance.getString("DzienTekst")).inMillis val lessonDate = Date.fromMillis(lessonDateMillis) + val startTime = data.lessonRanges.get(attendance.getInt("Numer") ?: 0)?.startTime - val lessonSemester = profile.dateToSemester(lessonDate) + val lessonSemester = semesterNumber val attendanceObject = Attendance( - profileId, - id.toLong(), - -1, - attendance.getLong("IdPrzedmiot") ?: -1, - lessonSemester, - attendance.getString("PrzedmiotNazwa") + attendanceCategory.name.let { " - $it" }, - lessonDate, - data.lessonRanges.get(attendance.getInt("Numer") ?: 0)?.startTime, - type) + profileId = profileId, + id = id.toLong(), + baseType = type.baseType, + typeName = type.typeName, + typeShort = type.typeShort, + typeSymbol = type.typeSymbol, + typeColor = type.typeColor, + date = lessonDate, + startTime = startTime, + semester = lessonSemester, + teacherId = -1, + subjectId = attendance.getLong("IdPrzedmiot") ?: -1, + addedDate = lessonDate.combineWith(startTime) + ).also { + it.lessonNumber = attendance.getInt("Numer") + it.isCounted = it.baseType != Attendance.TYPE_RELEASED + } data.attendanceList.add(attendanceObject) - if (attendanceObject.type != TYPE_PRESENT) { + if (type.baseType != TYPE_PRESENT) { data.metadataList.add(Metadata( profileId, Metadata.TYPE_ATTENDANCE, attendanceObject.id, - profile.empty, - profile.empty, - attendanceObject.lessonDate.combineWith(attendanceObject.startTime) + profile.empty || type.baseType == Attendance.TYPE_PRESENT_CUSTOM || type.baseType == Attendance.TYPE_UNKNOWN, + profile.empty || type.baseType == Attendance.TYPE_PRESENT_CUSTOM || type.baseType == Attendance.TYPE_UNKNOWN )) } } - data.setSyncNext(ENDPOINT_VULCAN_API_ATTENDANCE, SYNC_ALWAYS) - onSuccess(ENDPOINT_VULCAN_API_ATTENDANCE) + onSuccess() } - } ?: onSuccess(ENDPOINT_VULCAN_API_ATTENDANCE) } + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiDictionaries.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiDictionaries.kt index 76f53cf5..2dc9f6bc 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiDictionaries.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiDictionaries.kt @@ -114,11 +114,11 @@ class VulcanApiDictionaries(override val data: DataVulcan, private fun saveAttendanceType(attendanceType: JsonObject) { val id = attendanceType.getLong("Id") ?: return - val name = attendanceType.getString("Nazwa") ?: "" + val typeName = attendanceType.getString("Nazwa") ?: "" val absent = attendanceType.getBoolean("Nieobecnosc") ?: false val excused = attendanceType.getBoolean("Usprawiedliwione") ?: false - val type = if (absent) { + val baseType = if (absent) { if (excused) Attendance.TYPE_ABSENT_EXCUSED else @@ -137,15 +137,35 @@ class VulcanApiDictionaries(override val data: DataVulcan, else if (present) Attendance.TYPE_PRESENT else - Attendance.TYPE_CUSTOM + Attendance.TYPE_UNKNOWN + } + + val (typeColor, typeSymbol) = when (id.toInt()) { + 1 -> 0xffffffff to "●" // obecność + 2 -> 0xffffa687 to "—" // nieobecność + 3 -> 0xfffcc150 to "u" // nieobecność usprawiedliwiona + 4 -> 0xffede049 to "s" // spóźnienie + 5 -> 0xffbbdd5f to "su" // spóźnienie usprawiedliwione + 6 -> 0xffa9c9fd to "ns" // nieobecny z przyczyn szkolnych + 7 -> 0xffddbbe5 to "z" // zwolniony + 8 -> 0xffffffff to "" // usunięty wpis + else -> null to "?" + } + + val typeShort = when (id.toInt()) { + 6 -> "ns" // nieobecny z przyczyn szkolnych + 8 -> "" // usunięty wpis + else -> data.app.attendanceManager.getTypeShort(baseType) } val attendanceTypeObject = AttendanceType( profileId, id, - name, - type, - -1 + baseType, + typeName, + typeShort, + typeSymbol, + typeColor?.toInt() ) data.attendanceTypes.put(id, attendanceTypeObject) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiEvents.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiEvents.kt index 4ba7c460..6f045828 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiEvents.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiEvents.kt @@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.Metadata +import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.getBoolean import pl.szczodrzynski.edziennik.getJsonArray @@ -31,11 +32,43 @@ class VulcanApiEvents(override val data: DataVulcan, init { data.profile?.also { profile -> - val startDate: String = when (profile.empty) { - true -> profile.getSemesterStart(profile.currentSemester).stringY_m_d + val semesterId = data.studentSemesterId + val semesterNumber = data.studentSemesterNumber + if (semesterNumber == 2 && lastSync ?: 0 < profile.dateSemester1Start.inMillis) { + getEvents(profile, semesterId - 1, semesterNumber - 1) { + getEvents(profile, semesterId, semesterNumber) { + finish() + } + } + } + else { + getEvents(profile, semesterId, semesterNumber) { + finish() + } + } + + } ?: onSuccess(if (isHomework) ENDPOINT_VULCAN_API_HOMEWORK else ENDPOINT_VULCAN_API_EVENTS) } + + private fun finish() { + when (isHomework) { + true -> { + data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK)) + data.setSyncNext(ENDPOINT_VULCAN_API_HOMEWORK, SYNC_ALWAYS) + } + false -> { + data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK)) + data.setSyncNext(ENDPOINT_VULCAN_API_EVENTS, SYNC_ALWAYS) + } + } + onSuccess(if (isHomework) ENDPOINT_VULCAN_API_HOMEWORK else ENDPOINT_VULCAN_API_EVENTS) + } + + private fun getEvents(profile: Profile, semesterId: Int, semesterNumber: Int, onSuccess: () -> Unit) { + val startDate = when (profile.empty) { + true -> profile.getSemesterStart(semesterNumber).stringY_m_d else -> Date.getToday().stepForward(0, -1, 0).stringY_m_d } - val endDate: String = profile.getSemesterEnd(profile.currentSemester).stringY_m_d + val endDate = profile.getSemesterEnd(semesterNumber).stringY_m_d val endpoint = when (isHomework) { true -> VULCAN_API_ENDPOINT_HOMEWORK @@ -46,7 +79,7 @@ class VulcanApiEvents(override val data: DataVulcan, "DataKoncowa" to endDate, "IdOddzial" to data.studentClassId, "IdUczen" to data.studentId, - "IdOkresKlasyfikacyjny" to data.studentSemesterId + "IdOkresKlasyfikacyjny" to semesterId )) { json, _ -> val events = json.getJsonArray("Data") @@ -59,7 +92,7 @@ class VulcanApiEvents(override val data: DataVulcan, val teacherId = event.getLong("IdPracownik") ?: -1 val topic = event.getString("Opis")?.trim() ?: "" - val lessonList = data.db.timetableDao().getForDateNow(profileId, eventDate) + val lessonList = data.db.timetableDao().getAllForDateNow(profileId, eventDate) val startTime = lessonList.firstOrNull { it.subjectId == subjectId }?.startTime val type = when (isHomework) { @@ -90,22 +123,11 @@ class VulcanApiEvents(override val data: DataVulcan, if (isHomework) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT, id, profile.empty, - profile.empty, - System.currentTimeMillis() + profile.empty )) } - when (isHomework) { - true -> { - data.toRemove.add(DataRemoveModel.Events.futureWithType(Event.TYPE_HOMEWORK)) - data.setSyncNext(ENDPOINT_VULCAN_API_HOMEWORK, SYNC_ALWAYS) - } - false -> { - data.toRemove.add(DataRemoveModel.Events.futureExceptType(Event.TYPE_HOMEWORK)) - data.setSyncNext(ENDPOINT_VULCAN_API_EVENTS, SYNC_ALWAYS) - } - } - onSuccess(if (isHomework) ENDPOINT_VULCAN_API_HOMEWORK else ENDPOINT_VULCAN_API_EVENTS) + onSuccess() } - } ?: onSuccess(if (isHomework) ENDPOINT_VULCAN_API_HOMEWORK else ENDPOINT_VULCAN_API_EVENTS) } + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiGrades.kt index f62778fc..9a354ea5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiGrades.kt @@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.api.models.DataRemoveModel import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL import pl.szczodrzynski.edziennik.data.db.entity.Metadata +import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import java.text.DecimalFormat import kotlin.math.roundToInt @@ -27,9 +28,33 @@ class VulcanApiGrades(override val data: DataVulcan, init { data.profile?.also { profile -> + val semesterId = data.studentSemesterId + val semesterNumber = data.studentSemesterNumber + if (semesterNumber == 2 && lastSync ?: 0 < profile.dateSemester1Start.inMillis) { + getGrades(profile, semesterId - 1, semesterNumber - 1) { + getGrades(profile, semesterId, semesterNumber) { + finish() + } + } + } + else { + getGrades(profile, semesterId, semesterNumber) { + finish() + } + } + + } ?: onSuccess(ENDPOINT_VULCAN_API_GRADES) } + + private fun finish() { + data.toRemove.add(DataRemoveModel.Grades.semesterWithType(data.studentSemesterNumber, TYPE_NORMAL)) + data.setSyncNext(ENDPOINT_VULCAN_API_GRADES, SYNC_ALWAYS) + onSuccess(ENDPOINT_VULCAN_API_GRADES) + } + + private fun getGrades(profile: Profile, semesterId: Int, semesterNumber: Int, onSuccess: () -> Unit) { apiGet(TAG, VULCAN_API_ENDPOINT_GRADES, parameters = mapOf( "IdUczen" to data.studentId, - "IdOkresKlasyfikacyjny" to data.studentSemesterId + "IdOkresKlasyfikacyjny" to semesterId )) { json, _ -> val grades = json.getJsonArray("Data") @@ -99,9 +124,10 @@ class VulcanApiGrades(override val data: DataVulcan, category = category, description = finalDescription, comment = null, - semester = data.studentSemesterNumber, + semester = semesterNumber, teacherId = teacherId, - subjectId = subjectId + subjectId = subjectId, + addedDate = addedDate ) data.gradeList.add(gradeObject) @@ -110,15 +136,11 @@ class VulcanApiGrades(override val data: DataVulcan, Metadata.TYPE_GRADE, id, profile.empty, - profile.empty, - addedDate - + profile.empty )) } - data.toRemove.add(DataRemoveModel.Grades.semesterWithType(data.studentSemesterNumber, Grade.TYPE_NORMAL)) - data.setSyncNext(ENDPOINT_VULCAN_API_GRADES, SYNC_ALWAYS) - onSuccess(ENDPOINT_VULCAN_API_GRADES) + onSuccess() } - } ?: onSuccess(ENDPOINT_VULCAN_API_GRADES) } + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesChangeStatus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesChangeStatus.kt index 5e226849..b0b7795d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesChangeStatus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesChangeStatus.kt @@ -37,8 +37,7 @@ class VulcanApiMessagesChangeStatus(override val data: DataVulcan, Metadata.TYPE_MESSAGE, messageObject.id, true, - true, - messageObject.addedDate + true )) messageObject.seen = true diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesInbox.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesInbox.kt index 388a97ad..dea913c5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesInbox.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesInbox.kt @@ -72,7 +72,8 @@ class VulcanApiMessagesInbox(override val data: DataVulcan, type = TYPE_RECEIVED, subject = subject, body = body.replace("\n", "
"), - senderId = senderId + senderId = senderId, + addedDate = sentDate ) val messageRecipientObject = MessageRecipient( @@ -90,8 +91,7 @@ class VulcanApiMessagesInbox(override val data: DataVulcan, Metadata.TYPE_MESSAGE, id, readDate > 0, - readDate > 0, - sentDate + readDate > 0 )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesSent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesSent.kt index d190c00f..94cfdcba 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesSent.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiMessagesSent.kt @@ -97,7 +97,8 @@ class VulcanApiMessagesSent(override val data: DataVulcan, type = TYPE_SENT, subject = subject, body = body.replace("\n", "
"), - senderId = null + senderId = null, + addedDate = sentDate ) data.messageList.add(messageObject) @@ -106,8 +107,7 @@ class VulcanApiMessagesSent(override val data: DataVulcan, Metadata.TYPE_MESSAGE, id, true, - true, - sentDate + true )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiNotices.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiNotices.kt index b2202981..45155a5b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiNotices.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiNotices.kt @@ -11,12 +11,12 @@ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_API_ import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanApi import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.Notice +import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.getJsonArray import pl.szczodrzynski.edziennik.getLong import pl.szczodrzynski.edziennik.getString import pl.szczodrzynski.edziennik.toSparseArray -import pl.szczodrzynski.edziennik.utils.models.Date class VulcanApiNotices(override val data: DataVulcan, override val lastSync: Long?, @@ -31,6 +31,29 @@ class VulcanApiNotices(override val data: DataVulcan, data.db.noticeTypeDao().getAllNow(profileId).toSparseArray(data.noticeTypes) { it.id } } + val semesterId = data.studentSemesterId + val semesterNumber = data.studentSemesterNumber + if (semesterNumber == 2 && lastSync ?: 0 < profile.dateSemester1Start.inMillis) { + getNotices(profile, semesterId - 1, semesterNumber - 1) { + getNotices(profile, semesterId, semesterNumber) { + finish() + } + } + } + else { + getNotices(profile, semesterId, semesterNumber) { + finish() + } + } + + } ?: onSuccess(ENDPOINT_VULCAN_API_NOTICES) } + + private fun finish() { + data.setSyncNext(ENDPOINT_VULCAN_API_NOTICES, SYNC_ALWAYS) + onSuccess(ENDPOINT_VULCAN_API_NOTICES) + } + + private fun getNotices(profile: Profile, semesterId: Int, semesterNumber: Int, onSuccess: () -> Unit) { apiGet(TAG, VULCAN_API_ENDPOINT_NOTICES, parameters = mapOf( "IdUczen" to data.studentId, "IdOkresKlasyfikacyjny" to data.studentSemesterId @@ -41,15 +64,21 @@ class VulcanApiNotices(override val data: DataVulcan, val id = notice.getLong("Id") ?: return@forEach val text = notice.getString("TrescUwagi") ?: return@forEach val teacherId = notice.getLong("IdPracownik") ?: -1 - val addedDate = Date.fromY_m_d(notice.getString("DataWpisuTekst")).inMillis + val addedDate = notice.getLong("DataModyfikacji")?.times(1000) ?: System.currentTimeMillis() + + val categoryId = notice.getLong("IdKategoriaUwag") ?: -1 + val categoryText = data.noticeTypes[categoryId]?.name ?: "" val noticeObject = Notice( - profileId, - id, - text, - profile.currentSemester, - Notice.TYPE_NEUTRAL, - teacherId + profileId = profileId, + id = id, + type = Notice.TYPE_NEUTRAL, + semester = profile.currentSemester, + text = text, + category = categoryText, + points = null, + teacherId = teacherId, + addedDate = addedDate ) data.noticeList.add(noticeObject) @@ -58,13 +87,11 @@ class VulcanApiNotices(override val data: DataVulcan, Metadata.TYPE_NOTICE, id, profile.empty, - profile.empty, - addedDate + profile.empty )) } - data.setSyncNext(ENDPOINT_VULCAN_API_NOTICES, SYNC_ALWAYS) - onSuccess(ENDPOINT_VULCAN_API_NOTICES) + onSuccess() } - } ?: onSuccess(ENDPOINT_VULCAN_API_NOTICES) } + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiProposedGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiProposedGrades.kt index ee1e250f..e897df92 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiProposedGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiProposedGrades.kt @@ -13,6 +13,7 @@ import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER1_ import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_FINAL import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_SEMESTER2_PROPOSED import pl.szczodrzynski.edziennik.data.db.entity.Metadata +import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.getJsonArray import pl.szczodrzynski.edziennik.getJsonObject import pl.szczodrzynski.edziennik.utils.Utils @@ -27,32 +28,54 @@ class VulcanApiProposedGrades(override val data: DataVulcan, init { data.profile?.also { profile -> + val semesterId = data.studentSemesterId + val semesterNumber = data.studentSemesterNumber + if (semesterNumber == 2 && lastSync ?: 0 < profile.dateSemester1Start.inMillis) { + getProposedGrades(profile, semesterId - 1, semesterNumber - 1) { + getProposedGrades(profile, semesterId, semesterNumber) { + finish() + } + } + } + else { + getProposedGrades(profile, semesterId, semesterNumber) { + finish() + } + } + + } ?: onSuccess(ENDPOINT_VULCAN_API_GRADES_SUMMARY) } + + private fun finish() { + data.setSyncNext(ENDPOINT_VULCAN_API_GRADES_SUMMARY, 6*HOUR) + onSuccess(ENDPOINT_VULCAN_API_GRADES_SUMMARY) + } + + private fun getProposedGrades(profile: Profile, semesterId: Int, semesterNumber: Int, onSuccess: () -> Unit) { apiGet(TAG, VULCAN_API_ENDPOINT_GRADES_PROPOSITIONS, parameters = mapOf( "IdUczen" to data.studentId, - "IdOkresKlasyfikacyjny" to data.studentSemesterId + "IdOkresKlasyfikacyjny" to semesterId )) { json, _ -> val grades = json.getJsonObject("Data") grades.getJsonArray("OcenyPrzewidywane")?.let { - processGradeList(it, isFinal = false) + processGradeList(it, semesterNumber, isFinal = false) } grades.getJsonArray("OcenyKlasyfikacyjne")?.let { - processGradeList(it, isFinal = true) + processGradeList(it, semesterNumber, isFinal = true) } - data.setSyncNext(ENDPOINT_VULCAN_API_GRADES_SUMMARY, 6*HOUR) - onSuccess(ENDPOINT_VULCAN_API_GRADES_SUMMARY) + onSuccess() } - } ?: onSuccess(ENDPOINT_VULCAN_API_GRADES_SUMMARY) } + } - private fun processGradeList(grades: JsonArray, isFinal: Boolean) { - grades.asJsonObjectList()?.forEach { grade -> + private fun processGradeList(grades: JsonArray, semesterNumber: Int, isFinal: Boolean) { + grades.asJsonObjectList().forEach { grade -> val name = grade.get("Wpis").asString val value = Utils.getGradeValue(name) val subjectId = grade.get("IdPrzedmiot").asLong - val id = subjectId * -100 - data.studentSemesterNumber + val id = subjectId * -100 - semesterNumber val color = Utils.getVulcanGradeColor(name) @@ -60,7 +83,7 @@ class VulcanApiProposedGrades(override val data: DataVulcan, profileId = profileId, id = id, name = name, - type = if (data.studentSemesterNumber == 1) { + type = if (semesterNumber == 1) { if (isFinal) TYPE_SEMESTER1_FINAL else TYPE_SEMESTER1_PROPOSED } else { if (isFinal) TYPE_SEMESTER2_FINAL else TYPE_SEMESTER2_PROPOSED @@ -71,7 +94,7 @@ class VulcanApiProposedGrades(override val data: DataVulcan, category = "", description = null, comment = null, - semester = data.studentSemesterNumber, + semester = semesterNumber, teacherId = -1, subjectId = subjectId ) @@ -82,8 +105,7 @@ class VulcanApiProposedGrades(override val data: DataVulcan, Metadata.TYPE_GRADE, gradeObject.id, data.profile?.empty ?: false, - data.profile?.empty ?: false, - System.currentTimeMillis() + data.profile?.empty ?: false )) } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiSendMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiSendMessage.kt index b88ccdb6..8c89b620 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiSendMessage.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiSendMessage.kt @@ -54,7 +54,7 @@ class VulcanApiSendMessage(override val data: DataVulcan, VulcanApiMessagesSent(data, null) { val message = data.messageList.firstOrNull { it.type == Message.TYPE_SENT && it.subject == subject } val metadata = data.metadataList.firstOrNull { it.thingType == Metadata.TYPE_MESSAGE && it.thingId == messageId } - val event = MessageSentEvent(data.profileId, message, metadata?.addedDate) + val event = MessageSentEvent(data.profileId, message, message?.addedDate) EventBus.getDefault().postSticky(event) onSuccess() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiTimetable.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiTimetable.kt index 70830865..46b52de5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiTimetable.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiTimetable.kt @@ -184,8 +184,7 @@ class VulcanApiTimetable(override val data: DataVulcan, Metadata.TYPE_LESSON_CHANGE, lessonObject.id, seen, - seen, - System.currentTimeMillis() + seen )) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiUpdateSemester.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiUpdateSemester.kt index 1f03484c..50fb178a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiUpdateSemester.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/api/VulcanApiUpdateSemester.kt @@ -60,6 +60,7 @@ class VulcanApiUpdateSemester(override val data: DataVulcan, data.studentClassId = studentClassId data.studentSemesterId = studentSemesterId data.studentSemesterNumber = studentSemesterNumber + data.profile.studentData["semester${studentSemesterNumber}Id"] = studentSemesterId data.currentSemesterEndDate = currentSemesterEndDate profile.studentClassName = studentClassName dateSemester1Start?.let { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/firstlogin/VulcanFirstLogin.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/firstlogin/VulcanFirstLogin.kt index 00eb7f24..ed541c69 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/firstlogin/VulcanFirstLogin.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/firstlogin/VulcanFirstLogin.kt @@ -177,6 +177,7 @@ class VulcanFirstLogin(val data: DataVulcan, val onSuccess: () -> Unit) { studentData["studentClassId"] = studentClassId studentData["studentSemesterId"] = studentSemesterId studentData["studentSemesterNumber"] = studentSemesterNumber + studentData["semester${studentSemesterNumber}Id"] = studentSemesterId studentData["schoolSymbol"] = schoolSymbol studentData["schoolShort"] = schoolShort studentData["schoolName"] = schoolCode diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginApi.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginApi.kt index cdf93c5d..0cf1c895 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginApi.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/login/VulcanLoginApi.kt @@ -26,6 +26,11 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) { } init { run { + if (data.studentSemesterNumber == 1 && data.semester1Id == 0) + data.semester1Id = data.studentSemesterNumber + if (data.studentSemesterNumber == 2 && data.semester2Id == 0) + data.semester2Id = data.studentSemesterNumber + copyFromLoginStore() if (data.profile != null && data.isApiLoginValid()) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt index ed1fba00..6bd9aa83 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/Data.kt @@ -88,6 +88,7 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt var teacherOnConflictStrategy = OnConflictStrategy.IGNORE var eventListReplace = false var messageListReplace = false + var announcementListReplace = false val classrooms = LongSparseArray() val attendanceTypes = LongSparseArray() @@ -120,7 +121,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt val attendanceList = mutableListOf() val announcementList = mutableListOf() - val announcementIgnoreList = mutableListOf() val luckyNumberList = mutableListOf() @@ -178,7 +178,6 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt noticeList.clear() attendanceList.clear() announcementList.clear() - announcementIgnoreList.clear() luckyNumberList.clear() teacherAbsenceList.clear() messageList.clear() @@ -284,39 +283,19 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt d("Metadata saved in ${System.currentTimeMillis()-startTime} ms") startTime = System.currentTimeMillis() - if (lessonList.isNotEmpty()) { - db.timetableDao() += lessonList - } - if (gradeList.isNotEmpty()) { - db.gradeDao().addAll(gradeList) - } - if (eventList.isNotEmpty()) { - if (eventListReplace) - db.eventDao().replaceAll(eventList) - else - db.eventDao().upsertAll(eventList, removeNotKept = true) - } + db.timetableDao().putAll(lessonList, removeNotKept = true) + db.gradeDao().putAll(gradeList, removeNotKept = true) + db.eventDao().putAll(eventList, forceReplace = eventListReplace, removeNotKept = true) if (noticeList.isNotEmpty()) { db.noticeDao().clear(profile.id) - db.noticeDao().addAll(noticeList) + db.noticeDao().putAll(noticeList) } - if (attendanceList.isNotEmpty()) - db.attendanceDao().addAll(attendanceList) - if (announcementList.isNotEmpty()) - db.announcementDao().addAll(announcementList) - if (announcementIgnoreList.isNotEmpty()) - db.announcementDao().addAllIgnore(announcementIgnoreList) - if (luckyNumberList.isNotEmpty()) - db.luckyNumberDao().addAll(luckyNumberList) - if (teacherAbsenceList.isNotEmpty()) - db.teacherAbsenceDao().addAll(teacherAbsenceList) + db.attendanceDao().putAll(attendanceList, removeNotKept = true) + db.announcementDao().putAll(announcementList, forceReplace = announcementListReplace, removeNotKept = false) + db.luckyNumberDao().putAll(luckyNumberList) + db.teacherAbsenceDao().putAll(teacherAbsenceList) - if (messageList.isNotEmpty()) { - if (messageListReplace) - db.messageDao().replaceAll(messageList) - else - db.messageDao().upsertAll(messageList, removeNotKept = false) // TODO dataRemoveModel for messages - } + db.messageDao().putAll(messageList, forceReplace = messageListReplace, removeNotKept = false) if (messageRecipientList.isNotEmpty()) db.messageRecipientDao().addAll(messageRecipientList) if (messageRecipientIgnoreList.isNotEmpty()) @@ -357,7 +336,7 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt } fun shouldSyncLuckyNumber(): Boolean { - return (db.luckyNumberDao().getNearestFutureNow(profileId, Date.getToday().value) ?: -1) == -1 + return db.luckyNumberDao().getNearestFutureNow(profileId, Date.getToday()) == null } /*fun error(tag: String, errorCode: Int, response: Response? = null, throwable: Throwable? = null, apiResponse: JsonObject? = null) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/DataRemoveModel.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/DataRemoveModel.kt index f247ee19..fabf5f39 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/DataRemoveModel.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/models/DataRemoveModel.kt @@ -20,10 +20,10 @@ open class DataRemoveModel { fun commit(profileId: Int, dao: TimetableDao) { if (dateFrom != null && dateTo != null) { - dao.clearBetweenDates(profileId, dateFrom, dateTo) + dao.dontKeepBetweenDates(profileId, dateFrom, dateTo) } else { - dateFrom?.let { dateFrom -> dao.clearFromDate(profileId, dateFrom) } - dateTo?.let { dateTo -> dao.clearToDate(profileId, dateTo) } + dateFrom?.let { dateFrom -> dao.dontKeepFromDate(profileId, dateFrom) } + dateTo?.let { dateTo -> dao.dontKeepToDate(profileId, dateTo) } } } } @@ -69,7 +69,7 @@ open class DataRemoveModel { fun commit(profileId: Int, dao: AttendanceDao) { if (dateFrom != null) { - dao.clearAfterDate(profileId, dateFrom) + dao.dontKeepAfterDate(profileId, dateFrom) } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing.kt index 65a3be61..3ee19992 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/szkolny/interceptor/Signing.kt @@ -39,13 +39,13 @@ object Signing { val appPassword by lazy { iLoveApple( "ThisIsOurHardWorkPleaseDoNotCopyOrSteal(c)2019.KubaSz".sha256(), - BuildConfig.VERSION_NAME, + BuildConfig.VERSION_NAME.substringBeforeLast('+'), BuildConfig.VERSION_CODE.toLong() ) } /*fun provideKey(param1: String, param2: Long): ByteArray {*/ fun pleaseStopRightNow(param1: String, param2: Long): ByteArray { - return "$param1.MTIzNDU2Nzg5MDP/4SAI6B===.$param2".sha256() + return "$param1.MTIzNDU2Nzg5MDwwzwp5Gx===.$param2".sha256() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/AppSync.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/AppSync.kt index 63d4ec88..c10bab3e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/AppSync.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/AppSync.kt @@ -40,8 +40,7 @@ class AppSync(val app: App, val notifications: MutableList, val pr Metadata.TYPE_EVENT, event.id, isPast || markAsSeen || event.seen, - isPast || markAsSeen || event.notified, - event.addedDate + isPast || markAsSeen || event.notified ) }) return app.db.eventDao().upsertAll(events).size diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt index 39b68499..21a5c112 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/task/Notifications.kt @@ -53,7 +53,7 @@ class Notifications(val app: App, val notifications: MutableList, profileId = lesson.profileId, profileName = profiles.singleOrNull { it.id == lesson.profileId }?.name, viewId = MainActivity.DRAWER_ITEM_TIMETABLE, - addedDate = lesson.addedDate + addedDate = System.currentTimeMillis() ).addExtra("timetableDate", lesson.displayDate?.stringY_m_d ?: "") } } @@ -117,7 +117,7 @@ class Notifications(val app: App, val notifications: MutableList, } private fun gradeNotifications() { - for (grade in app.db.gradeDao().notNotifiedNow) { + for (grade in app.db.gradeDao().getNotNotifiedNow()) { val gradeName = when (grade.type) { Grade.TYPE_SEMESTER1_PROPOSED, Grade.TYPE_SEMESTER2_PROPOSED -> app.getString(R.string.grade_semester_proposed_format_2, grade.name) Grade.TYPE_SEMESTER1_FINAL, Grade.TYPE_SEMESTER2_FINAL -> app.getString(R.string.grade_semester_final_format_2, grade.name) @@ -144,7 +144,7 @@ class Notifications(val app: App, val notifications: MutableList, } private fun behaviourNotifications() { - for (notice in app.db.noticeDao().notNotifiedNow) { + for (notice in app.db.noticeDao().getNotNotifiedNow()) { val noticeTypeStr = when (notice.type) { Notice.TYPE_POSITIVE -> app.getString(R.string.notification_notice_praise) @@ -155,7 +155,7 @@ class Notifications(val app: App, val notifications: MutableList, val text = app.getString( R.string.notification_notice_format, noticeTypeStr, - notice.teacherFullName, + notice.teacherName, Date.fromMillis(notice.addedDate).formattedString ) notifications += Notification( @@ -172,9 +172,9 @@ class Notifications(val app: App, val notifications: MutableList, } private fun attendanceNotifications() { - for (attendance in app.db.attendanceDao().notNotifiedNow) { + for (attendance in app.db.attendanceDao().getNotNotifiedNow()) { - val attendanceTypeStr = when (attendance.type) { + val attendanceTypeStr = when (attendance.baseType) { Attendance.TYPE_ABSENT -> app.getString(R.string.notification_absence) Attendance.TYPE_ABSENT_EXCUSED -> app.getString(R.string.notification_absence_excused) Attendance.TYPE_BELATED -> app.getString(R.string.notification_belated) @@ -191,7 +191,7 @@ class Notifications(val app: App, val notifications: MutableList, R.string.notification_attendance_format, attendanceTypeStr, attendance.subjectLongName, - attendance.lessonDate.formattedString + attendance.date.formattedString ) notifications += Notification( id = Notification.buildId(attendance.profileId, Notification.TYPE_NEW_ATTENDANCE, attendance.id), @@ -207,10 +207,10 @@ class Notifications(val app: App, val notifications: MutableList, } private fun announcementNotifications() { - for (announcement in app.db.announcementDao().notNotifiedNow) { + for (announcement in app.db.announcementDao().getNotNotifiedNow()) { val text = app.getString( R.string.notification_announcement_format, - announcement.teacherFullName, + announcement.teacherName, announcement.subject ) notifications += Notification( @@ -247,9 +247,9 @@ class Notifications(val app: App, val notifications: MutableList, } private fun luckyNumberNotifications() { - val luckyNumbers = app.db.luckyNumberDao().notNotifiedNow - luckyNumbers?.removeAll { it.date < today } - luckyNumbers?.forEach { luckyNumber -> + val luckyNumbers = app.db.luckyNumberDao().getNotNotifiedNow().toMutableList() + luckyNumbers.removeAll { it.date < today } + luckyNumbers.forEach { luckyNumber -> val profile = profiles.singleOrNull { it.id == luckyNumber.profileId } ?: return@forEach val text = when (profile.studentNumber != -1 && profile.studentNumber == luckyNumber.number) { true -> when (luckyNumber.date.value) { @@ -271,7 +271,7 @@ class Notifications(val app: App, val notifications: MutableList, profileId = luckyNumber.profileId, profileName = profile.name, viewId = MainActivity.DRAWER_ITEM_HOME, - addedDate = luckyNumber.addedDate + addedDate = System.currentTimeMillis() ) } } @@ -280,7 +280,7 @@ class Notifications(val app: App, val notifications: MutableList, for (teacherAbsence in app.db.teacherAbsenceDao().getNotNotifiedNow()) { val message = app.getString( R.string.notification_teacher_absence_new_format, - teacherAbsence.teacherFullName + teacherAbsence.teacherName ) notifications += Notification( id = Notification.buildId(teacherAbsence.profileId, Notification.TYPE_TEACHER_ABSENCE, teacherAbsence.id), diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.kt index b57cc4d4..333967b0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/AppDb.kt @@ -43,7 +43,7 @@ import pl.szczodrzynski.edziennik.data.db.migration.* LibrusLesson::class, TimetableManual::class, Metadata::class -], version = 85) +], version = 88) @TypeConverters( ConverterTime::class, ConverterDate::class, @@ -170,7 +170,10 @@ abstract class AppDb : RoomDatabase() { Migration82(), Migration83(), Migration84(), - Migration85() + Migration85(), + Migration86(), + Migration87(), + Migration88() ).allowMainThreadQueries().build() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.java deleted file mode 100644 index ab32fb7c..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.dao; - -import androidx.lifecycle.LiveData; -import androidx.room.Dao; -import androidx.room.Insert; -import androidx.room.OnConflictStrategy; -import androidx.room.Query; -import androidx.room.RawQuery; -import androidx.sqlite.db.SimpleSQLiteQuery; -import androidx.sqlite.db.SupportSQLiteQuery; - -import java.util.List; - -import pl.szczodrzynski.edziennik.data.db.entity.Announcement; -import pl.szczodrzynski.edziennik.data.db.entity.Metadata; -import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull; - -import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ANNOUNCEMENT; - -@Dao -public abstract class AnnouncementDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - public abstract long add(Announcement announcement); - - @Insert(onConflict = OnConflictStrategy.REPLACE) - public abstract void addAll(List announcementList); - - @Insert(onConflict = OnConflictStrategy.IGNORE) - public abstract void addAllIgnore(List announcementList); - - @Query("DELETE FROM announcements WHERE profileId = :profileId") - public abstract void clear(int profileId); - - @RawQuery(observedEntities = {Announcement.class, Metadata.class}) - abstract LiveData> getAll(SupportSQLiteQuery query); - public LiveData> getAll(int profileId, String filter) { - return getAll(new SimpleSQLiteQuery("SELECT \n" + - "*, \n" + - "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" + - "FROM announcements \n" + - "LEFT JOIN teachers USING(profileId, teacherId)\n" + - "LEFT JOIN metadata ON announcementId = thingId AND thingType = "+TYPE_ANNOUNCEMENT+" AND metadata.profileId = "+profileId+"\n" + - "WHERE announcements.profileId = "+profileId+" AND "+filter+"\n" + - "ORDER BY addedDate DESC")); - } - public LiveData> getAll(int profileId) { - return getAll(profileId, "1"); - } - public LiveData> getAllWhere(int profileId, String filter) { - return getAll(profileId, filter); - } - - @RawQuery(observedEntities = {Announcement.class, Metadata.class}) - abstract List getAllNow(SupportSQLiteQuery query); - public List getAllNow(int profileId, String filter) { - return getAllNow(new SimpleSQLiteQuery("SELECT \n" + - "*, \n" + - "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" + - "FROM announcements \n" + - "LEFT JOIN teachers USING(profileId, teacherId)\n" + - "LEFT JOIN metadata ON announcementId = thingId AND thingType = "+TYPE_ANNOUNCEMENT+" AND metadata.profileId = "+profileId+"\n" + - "WHERE announcements.profileId = "+profileId+" AND "+filter+"\n" + - "ORDER BY addedDate DESC")); - } - public List getNotNotifiedNow(int profileId) { - return getAllNow(profileId, "notified = 0"); - } - - @Query("SELECT " + - "*, " + - "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName " + - "FROM announcements " + - "LEFT JOIN teachers USING(profileId, teacherId) " + - "LEFT JOIN metadata ON announcementId = thingId AND thingType = "+TYPE_ANNOUNCEMENT+" AND metadata.profileId = announcements.profileId " + - "WHERE notified = 0 " + - "ORDER BY addedDate DESC") - public abstract List getNotNotifiedNow(); -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.kt new file mode 100644 index 00000000..67e034c5 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AnnouncementDao.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-25. + */ +package pl.szczodrzynski.edziennik.data.db.dao + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Query +import androidx.room.RawQuery +import androidx.sqlite.db.SupportSQLiteQuery +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.annotation.SelectiveDao +import pl.szczodrzynski.edziennik.annotation.UpdateSelective +import pl.szczodrzynski.edziennik.data.db.AppDb +import pl.szczodrzynski.edziennik.data.db.entity.Announcement +import pl.szczodrzynski.edziennik.data.db.entity.Metadata +import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull + +@Dao +@SelectiveDao(db = AppDb::class) +abstract class AnnouncementDao : BaseDao { + companion object { + private const val QUERY = """ + SELECT + *, + teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName + FROM announcements + LEFT JOIN teachers USING(profileId, teacherId) + LEFT JOIN metadata ON announcementId = thingId AND thingType = ${Metadata.TYPE_ANNOUNCEMENT} AND metadata.profileId = announcements.profileId + """ + + private const val ORDER_BY = """ORDER BY addedDate DESC""" + } + + private val selective by lazy { AnnouncementDaoSelective(App.db) } + + @RawQuery(observedEntities = [Announcement::class]) + abstract override fun getRaw(query: SupportSQLiteQuery): LiveData> + @RawQuery(observedEntities = [Announcement::class]) + abstract override fun getOne(query: SupportSQLiteQuery): LiveData + + // SELECTIVE UPDATE + @UpdateSelective(primaryKeys = ["profileId", "announcementId"], skippedColumns = ["addedDate", "announcementText"]) + override fun update(item: Announcement) = selective.update(item) + override fun updateAll(items: List) = selective.updateAll(items) + + // CLEAR + @Query("DELETE FROM announcements WHERE profileId = :profileId") + abstract override fun clear(profileId: Int) + // REMOVE NOT KEPT + @Query("DELETE FROM announcements WHERE keep = 0") + abstract override fun removeNotKept() + + // GET ALL - LIVE DATA + fun getAll(profileId: Int) = + getRaw("$QUERY WHERE announcements.profileId = $profileId $ORDER_BY") + + // GET ALL - NOW + fun getAllNow(profileId: Int) = + getRawNow("$QUERY WHERE announcements.profileId = $profileId $ORDER_BY") + fun getNotNotifiedNow() = + getRawNow("$QUERY WHERE notified = 0 $ORDER_BY") + fun getNotNotifiedNow(profileId: Int) = + getRawNow("$QUERY WHERE announcements.profileId = $profileId AND notified = 0 $ORDER_BY") + + // GET ONE - NOW + fun getByIdNow(profileId: Int, id: Long) = + getOneNow("$QUERY WHERE announcements.profileId = $profileId AND announcementId = $id") +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.java deleted file mode 100644 index 95c3147f..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.dao; - -import androidx.lifecycle.LiveData; -import androidx.room.Dao; -import androidx.room.Insert; -import androidx.room.OnConflictStrategy; -import androidx.room.Query; -import androidx.room.RawQuery; -import androidx.sqlite.db.SimpleSQLiteQuery; -import androidx.sqlite.db.SupportSQLiteQuery; - -import java.util.List; - -import pl.szczodrzynski.edziennik.data.db.entity.Attendance; -import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull; -import pl.szczodrzynski.edziennik.utils.models.Date; - -import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ATTENDANCE; - -@Dao -public abstract class AttendanceDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - public abstract long add(Attendance attendance); - - @Insert(onConflict = OnConflictStrategy.REPLACE) - public abstract void addAll(List attendanceList); - - @Query("DELETE FROM attendances WHERE profileId = :profileId") - public abstract void clear(int profileId); - - @Query("DELETE FROM attendances WHERE profileId = :profileId AND attendanceLessonDate > :date") - public abstract void clearAfterDate(int profileId, Date date); - - @RawQuery(observedEntities = {Attendance.class}) - abstract LiveData> getAll(SupportSQLiteQuery query); - public LiveData> getAll(int profileId, String filter) { - return getAll(new SimpleSQLiteQuery("SELECT \n" + - "*, \n" + - "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" + - "FROM attendances \n" + - "LEFT JOIN teachers USING(profileId, teacherId)\n" + - "LEFT JOIN subjects USING(profileId, subjectId)\n" + - "LEFT JOIN metadata ON attendanceId = thingId AND thingType = " + TYPE_ATTENDANCE + " AND metadata.profileId = "+profileId+"\n" + - "WHERE attendances.profileId = "+profileId+" AND "+filter+"\n" + - "ORDER BY attendanceLessonDate DESC, attendanceStartTime DESC")); - } - public LiveData> getAll(int profileId) { - return getAll(profileId, "1"); - } - public LiveData> getAllWhere(int profileId, String filter) { - return getAll(profileId, filter); - } - - @RawQuery - abstract List getAllNow(SupportSQLiteQuery query); - public List getAllNow(int profileId, String filter) { - return getAllNow(new SimpleSQLiteQuery("SELECT \n" + - "*, \n" + - "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" + - "FROM attendances \n" + - "LEFT JOIN teachers USING(profileId, teacherId)\n" + - "LEFT JOIN subjects USING(profileId, subjectId)\n" + - "LEFT JOIN metadata ON attendanceId = thingId AND thingType = " + TYPE_ATTENDANCE + " AND metadata.profileId = "+profileId+"\n" + - "WHERE attendances.profileId = "+profileId+" AND "+filter+"\n" + - "ORDER BY attendanceLessonDate DESC, attendanceStartTime DESC")); - } - public List getNotNotifiedNow(int profileId) { - return getAllNow(profileId, "notified = 0"); - } - - @Query("SELECT * FROM attendances " + - "LEFT JOIN subjects USING(profileId, subjectId) " + - "LEFT JOIN metadata ON attendanceId = thingId AND thingType = " + TYPE_ATTENDANCE + " AND metadata.profileId = attendances.profileId " + - "WHERE notified = 0 " + - "ORDER BY attendanceLessonDate DESC, attendanceStartTime DESC") - public abstract List getNotNotifiedNow(); - - // only absent and absent_excused count as absences - // all the other types are counted as being present - @Query("SELECT \n" + - "CAST(SUM(CASE WHEN attendanceType != "+Attendance.TYPE_ABSENT+" AND attendanceType != "+ Attendance.TYPE_ABSENT_EXCUSED+" THEN 1 ELSE 0 END) AS float)\n" + - " / \n" + - "CAST(count() AS float)*100 \n" + - "FROM attendances \n" + - "WHERE profileId = :profileId") - public abstract LiveData getAttendancePercentage(int profileId); - - @Query("SELECT \n" + - "CAST(SUM(CASE WHEN attendanceType != "+Attendance.TYPE_ABSENT+" AND attendanceType != "+Attendance.TYPE_ABSENT_EXCUSED+" THEN 1 ELSE 0 END) AS float)\n" + - " / \n" + - "CAST(count() AS float)*100 \n" + - "FROM attendances \n" + - "WHERE profileId = :profileId AND attendanceSemester = :semester") - public abstract float getAttendancePercentageNow(int profileId, int semester); - - @Query("SELECT \n" + - "CAST(SUM(CASE WHEN attendanceType != "+Attendance.TYPE_ABSENT+" AND attendanceType != "+Attendance.TYPE_ABSENT_EXCUSED+" THEN 1 ELSE 0 END) AS float)\n" + - " / \n" + - "CAST(count() AS float)*100 \n" + - "FROM attendances \n" + - "WHERE profileId = :profileId") - public abstract float getAttendancePercentageNow(int profileId); -} - diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.kt new file mode 100644 index 00000000..97513689 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/AttendanceDao.kt @@ -0,0 +1,74 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-24. + */ +package pl.szczodrzynski.edziennik.data.db.dao + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Query +import androidx.room.RawQuery +import androidx.sqlite.db.SupportSQLiteQuery +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.annotation.SelectiveDao +import pl.szczodrzynski.edziennik.annotation.UpdateSelective +import pl.szczodrzynski.edziennik.data.db.AppDb +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.data.db.entity.Metadata +import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull +import pl.szczodrzynski.edziennik.utils.models.Date + +@Dao +@SelectiveDao(db = AppDb::class) +abstract class AttendanceDao : BaseDao { + companion object { + private const val QUERY = """ + SELECT + *, + teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName + FROM attendances + LEFT JOIN teachers USING(profileId, teacherId) + LEFT JOIN subjects USING(profileId, subjectId) + LEFT JOIN metadata ON attendanceId = thingId AND thingType = ${Metadata.TYPE_ATTENDANCE} AND metadata.profileId = attendances.profileId + """ + + private const val ORDER_BY = """ORDER BY attendanceDate DESC, attendanceTime DESC""" + } + + private val selective by lazy { AttendanceDaoSelective(App.db) } + + @RawQuery(observedEntities = [Attendance::class]) + abstract override fun getRaw(query: SupportSQLiteQuery): LiveData> + @RawQuery(observedEntities = [Attendance::class]) + abstract override fun getOne(query: SupportSQLiteQuery): LiveData + + // SELECTIVE UPDATE + @UpdateSelective(primaryKeys = ["profileId", "attendanceId"], skippedColumns = ["addedDate", "announcementText"]) + override fun update(item: Attendance) = selective.update(item) + override fun updateAll(items: List) = selective.updateAll(items) + + // CLEAR + @Query("DELETE FROM attendances WHERE profileId = :profileId") + abstract override fun clear(profileId: Int) + // REMOVE NOT KEPT + @Query("DELETE FROM attendances WHERE keep = 0") + abstract override fun removeNotKept() + + // GET ALL - LIVE DATA + fun getAll(profileId: Int) = + getRaw("$QUERY WHERE attendances.profileId = $profileId $ORDER_BY") + + // GET ALL - NOW + fun getAllNow(profileId: Int) = + getRawNow("$QUERY WHERE attendances.profileId = $profileId $ORDER_BY") + fun getNotNotifiedNow() = + getRawNow("$QUERY WHERE notified = 0 $ORDER_BY") + fun getNotNotifiedNow(profileId: Int) = + getRawNow("$QUERY WHERE attendances.profileId = $profileId AND notified = 0 $ORDER_BY") + + // GET ONE - NOW + fun getByIdNow(profileId: Int, id: Long) = + getOneNow("$QUERY WHERE attendances.profileId = $profileId AND attendanceId = $id") + + @Query("UPDATE attendances SET keep = 0 WHERE profileId = :profileId AND attendanceDate >= :date") + abstract fun dontKeepAfterDate(profileId: Int, date: Date?) +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/BaseDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/BaseDao.kt index 8c13dca9..0ae961db 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/BaseDao.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/BaseDao.kt @@ -16,13 +16,15 @@ interface BaseDao { fun getRaw(query: SupportSQLiteQuery): LiveData> fun getRaw(query: String) = getRaw(SimpleSQLiteQuery(query)) @RawQuery + fun getOne(query: SupportSQLiteQuery): LiveData + fun getOne(query: String) = getOne(SimpleSQLiteQuery(query)) + @RawQuery fun getRawNow(query: SupportSQLiteQuery): List fun getRawNow(query: String) = getRawNow(SimpleSQLiteQuery(query)) @RawQuery fun getOneNow(query: SupportSQLiteQuery): F? fun getOneNow(query: String) = getOneNow(SimpleSQLiteQuery(query)) - @Query("DELETE FROM events WHERE keep = 0") fun removeNotKept() /** @@ -41,12 +43,14 @@ interface BaseDao { /** * REPLACE an [item] in the database, * removing any conflicting rows. + * Creates the item if it does not exist yet. */ @Insert(onConflict = OnConflictStrategy.REPLACE) fun replace(item: T) /** * REPLACE [items] in the database, * removing any conflicting rows. + * Creates items if it does not exist yet. */ @Insert(onConflict = OnConflictStrategy.REPLACE) fun replaceAll(items: List) @@ -97,4 +101,23 @@ interface BaseDao { if (removeNotKept) removeNotKept() return insertResult } + + /** + * Make sure that [items] are in the database. + * When [forceReplace] == false, do a selective update (UPSERT). + * When [forceReplace] == true, add all items replacing any conflicting ones (REPLACE). + * + * @param forceReplace whether to replace all items instead of selectively updating + * @param removeNotKept whether to remove all items whose [keep] parameter is false + */ + fun putAll(items: List, forceReplace: Boolean = false, removeNotKept: Boolean = false) { + if (items.isEmpty()) + return + if (forceReplace) + replaceAll(items) + else + upsertAll(items, removeNotKept = false) + + if (removeNotKept) removeNotKept() + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt index b2017cc5..44b2962d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/EventDao.kt @@ -31,8 +31,8 @@ abstract class EventDao : BaseDao { eventTypes.eventTypeName AS typeName, eventTypes.eventTypeColor AS typeColor FROM events - LEFT JOIN subjects USING(profileId, subjectId) LEFT JOIN teachers USING(profileId, teacherId) + LEFT JOIN subjects USING(profileId, subjectId) LEFT JOIN teams USING(profileId, teamId) LEFT JOIN eventTypes USING(profileId, eventType) LEFT JOIN metadata ON eventId = thingId AND (thingType = ${Metadata.TYPE_EVENT} OR thingType = ${Metadata.TYPE_HOMEWORK}) AND metadata.profileId = events.profileId @@ -47,6 +47,8 @@ abstract class EventDao : BaseDao { @RawQuery(observedEntities = [Event::class]) abstract override fun getRaw(query: SupportSQLiteQuery): LiveData> + @RawQuery(observedEntities = [Event::class]) + abstract override fun getOne(query: SupportSQLiteQuery): LiveData // SELECTIVE UPDATE @UpdateSelective(primaryKeys = ["profileId", "eventId"], skippedColumns = ["eventIsDone", "eventBlacklisted", "homeworkBody", "attachmentIds", "attachmentNames"]) @@ -56,6 +58,9 @@ abstract class EventDao : BaseDao { // CLEAR @Query("DELETE FROM events WHERE profileId = :profileId") abstract override fun clear(profileId: Int) + // REMOVE NOT KEPT + @Query("DELETE FROM events WHERE keep = 0") + abstract override fun removeNotKept() // GET ALL - LIVE DATA fun getAll(profileId: Int) = diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/GradeDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/GradeDao.kt index 9c3e93c5..5862859e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/GradeDao.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/GradeDao.kt @@ -1,32 +1,86 @@ /* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 + * Copyright (c) Kuba Szczodrzyński 2020-4-24. */ package pl.szczodrzynski.edziennik.data.db.dao import androidx.lifecycle.LiveData -import androidx.room.* -import androidx.sqlite.db.SimpleSQLiteQuery +import androidx.room.Dao +import androidx.room.Query +import androidx.room.RawQuery +import androidx.room.Transaction import androidx.sqlite.db.SupportSQLiteQuery +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.annotation.SelectiveDao +import pl.szczodrzynski.edziennik.annotation.UpdateSelective +import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.full.GradeFull +import pl.szczodrzynski.edziennik.utils.models.Date import java.util.* -import kotlin.collections.List import kotlin.collections.component1 import kotlin.collections.component2 -import kotlin.collections.iterator import kotlin.collections.set @Dao -abstract class GradeDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun add(grade: Grade): Long +@SelectiveDao(db = AppDb::class) +abstract class GradeDao : BaseDao { + companion object { + private const val QUERY = """ + SELECT + *, + teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName + FROM grades + LEFT JOIN teachers USING(profileId, teacherId) + LEFT JOIN subjects USING(profileId, subjectId) + LEFT JOIN metadata ON gradeId = thingId AND thingType = ${Metadata.TYPE_GRADE} AND metadata.profileId = grades.profileId + """ - @Insert(onConflict = OnConflictStrategy.REPLACE) - abstract fun addAll(gradeList: List) + private const val ORDER_BY = """ORDER BY addedDate DESC""" + } + private val selective by lazy { GradeDaoSelective(App.db) } + + @RawQuery(observedEntities = [Grade::class]) + abstract override fun getRaw(query: SupportSQLiteQuery): LiveData> + @RawQuery(observedEntities = [Grade::class]) + abstract override fun getOne(query: SupportSQLiteQuery): LiveData + + // SELECTIVE UPDATE + @UpdateSelective(primaryKeys = ["profileId", "gradeId"], skippedColumns = ["addedDate", "gradeClassAverage"]) + override fun update(item: Grade) = selective.update(item) + override fun updateAll(items: List) = selective.updateAll(items) + + // CLEAR @Query("DELETE FROM grades WHERE profileId = :profileId") - abstract fun clear(profileId: Int) + abstract override fun clear(profileId: Int) + // REMOVE NOT KEPT + @Query("DELETE FROM grades WHERE keep = 0") + abstract override fun removeNotKept() + + // GET ALL - LIVE DATA + fun getAll(profileId: Int) = + getRaw("$QUERY WHERE grades.profileId = $profileId $ORDER_BY") + fun getAllFromDate(profileId: Int, date: Date) = + getRaw("$QUERY WHERE grades.profileId = $profileId AND addedDate > ${date.inMillis} $ORDER_BY") + fun getAllBySubject(profileId: Int, subjectId: Long) = + getRaw("$QUERY WHERE grades.profileId = $profileId AND subjectId = $subjectId $ORDER_BY") + fun getAllOrderBy(profileId: Int, orderBy: String) = + getRaw("$QUERY WHERE grades.profileId = $profileId ORDER BY $orderBy") + + // GET ALL - NOW + fun getAllNow(profileId: Int) = + getRawNow("$QUERY WHERE grades.profileId = $profileId $ORDER_BY") + fun getNotNotifiedNow() = + getRawNow("$QUERY WHERE notified = 0 $ORDER_BY") + fun getNotNotifiedNow(profileId: Int) = + getRawNow("$QUERY WHERE grades.profileId = $profileId AND notified = 0 $ORDER_BY") + fun getByParentIdNow(profileId: Int, parentId: Long) = + getRawNow("$QUERY WHERE grades.profileId = $profileId AND gradeParentId = $parentId $ORDER_BY") + + // GET ONE - NOW + fun getByIdNow(profileId: Int, id: Long) = + getOneNow("$QUERY WHERE grades.profileId = $profileId AND gradeId = $id") @Query("DELETE FROM grades WHERE profileId = :profileId AND gradeType = :type") abstract fun clearWithType(profileId: Int, type: Int) @@ -37,66 +91,13 @@ abstract class GradeDao { @Query("DELETE FROM grades WHERE profileId = :profileId AND gradeSemester = :semester AND gradeType = :type") abstract fun clearForSemesterWithType(profileId: Int, semester: Int, type: Int) - @RawQuery(observedEntities = [Grade::class]) - abstract fun getAll(query: SupportSQLiteQuery?): LiveData> - fun getAll(profileId: Int, filter: String, orderBy: String): LiveData> { - return getAll(SimpleSQLiteQuery("SELECT \n" + - "*, \n" + - "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" + - "FROM grades \n" + - "LEFT JOIN subjects USING(profileId, subjectId)\n" + - "LEFT JOIN teachers USING(profileId, teacherId)\n" + - "LEFT JOIN metadata ON gradeId = thingId AND thingType = " + Metadata.TYPE_GRADE + " AND metadata.profileId = " + profileId + "\n" + - "WHERE grades.profileId = " + profileId + " AND " + filter + "\n" + - "ORDER BY " + orderBy)) // TODO: 2019-04-30 why did I add sorting by gradeType??? - } - - fun getAllOrderBy(profileId: Int, orderBy: String): LiveData> { - return getAll(profileId, "1", orderBy) - } - - fun getAllWhere(profileId: Int, filter: String): LiveData> { - return getAll(profileId, filter, "addedDate DESC") - } - - @RawQuery - abstract fun getAllNow(query: SupportSQLiteQuery?): List - - fun getAllNow(profileId: Int, filter: String): List { - return getAllNow(SimpleSQLiteQuery("SELECT \n" + - "*, \n" + - "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" + - "FROM grades \n" + - "LEFT JOIN subjects USING(profileId, subjectId)\n" + - "LEFT JOIN teachers USING(profileId, teacherId)\n" + - "LEFT JOIN metadata ON gradeId = thingId AND thingType = " + Metadata.TYPE_GRADE + " AND metadata.profileId = " + profileId + "\n" + - "WHERE grades.profileId = " + profileId + " AND " + filter + "\n" + - "ORDER BY addedDate DESC")) - } - - fun getNotNotifiedNow(profileId: Int): List { - return getAllNow(profileId, "notified = 0") - } - - fun getAllWithParentIdNow(profileId: Int, parentId: Long): List { - return getAllNow(profileId, "gradeParentId = $parentId") - } - - @get:Query("SELECT * FROM grades " + - "LEFT JOIN subjects USING(profileId, subjectId) " + - "LEFT JOIN metadata ON gradeId = thingId AND thingType = " + Metadata.TYPE_GRADE + " AND metadata.profileId = grades.profileId " + - "WHERE notified = 0 " + - "ORDER BY addedDate DESC") - abstract val notNotifiedNow: List - - @RawQuery - abstract fun getNow(query: SupportSQLiteQuery): GradeFull? + // GRADE DETAILS - MOBIDZIENNIK @Query("UPDATE grades SET gradeClassAverage = :classAverage, gradeColor = :color WHERE profileId = :profileId AND gradeId = :gradeId") abstract fun updateDetailsById(profileId: Int, gradeId: Long, classAverage: Float, color: Int) - @Query("UPDATE metadata SET addedDate = :addedDate WHERE profileId = :profileId AND thingType = " + Metadata.TYPE_GRADE + " AND thingId = :gradeId") + @Query("UPDATE grades SET addedDate = :addedDate WHERE profileId = :profileId AND gradeId = :gradeId") abstract fun updateAddedDateById(profileId: Int, gradeId: Long, addedDate: Long) @Transaction @@ -118,7 +119,7 @@ abstract class GradeDao { @Query("SELECT gradeColor FROM grades WHERE profileId = :profileId ORDER BY gradeId") abstract fun getColors(profileId: Int): List - @Query("SELECT addedDate FROM metadata WHERE profileId = :profileId AND thingType = " + Metadata.TYPE_GRADE + " ORDER BY thingId") + @Query("SELECT addedDate FROM grades WHERE profileId = :profileId ORDER BY gradeId") abstract fun getAddedDates(profileId: Int): List @Transaction @@ -134,8 +135,4 @@ abstract class GradeDao { gradeAddedDates[gradeId] = addedDates.next() } } - - fun getAllFromDate(profileId: Int, date: Long): LiveData> { - return getAllWhere(profileId, "addedDate > $date") - } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.java deleted file mode 100644 index c1c33c23..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.dao; - -import androidx.annotation.Nullable; -import androidx.lifecycle.LiveData; -import androidx.room.Dao; -import androidx.room.Insert; -import androidx.room.OnConflictStrategy; -import androidx.room.Query; -import androidx.room.RawQuery; -import androidx.sqlite.db.SimpleSQLiteQuery; -import androidx.sqlite.db.SupportSQLiteQuery; - -import java.util.List; - -import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber; -import pl.szczodrzynski.edziennik.data.db.entity.Metadata; -import pl.szczodrzynski.edziennik.data.db.entity.Notice; -import pl.szczodrzynski.edziennik.data.db.full.LuckyNumberFull; -import pl.szczodrzynski.edziennik.utils.models.Date; - -import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_LUCKY_NUMBER; - -@Dao -public abstract class LuckyNumberDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - public abstract void add(LuckyNumber luckyNumber); - - @Insert(onConflict = OnConflictStrategy.REPLACE) - public abstract void addAll(List luckyNumberList); - - @Query("DELETE FROM luckyNumbers WHERE profileId = :profileId") - public abstract void clear(int profileId); - - @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate = :date") - public abstract LiveData getByDate(int profileId, Date date); - - @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate = :date") - public abstract LuckyNumber getByDateNow(int profileId, Date date); - - @Nullable - @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate >= :date ORDER BY luckyNumberDate DESC LIMIT 1") - public abstract LuckyNumber getNearestFutureNow(int profileId, int date); - - @Query("SELECT * FROM luckyNumbers WHERE profileId = :profileId AND luckyNumberDate >= :date ORDER BY luckyNumberDate DESC LIMIT 1") - public abstract LiveData getNearestFuture(int profileId, int date); - - @RawQuery(observedEntities = {LuckyNumber.class}) - abstract LiveData> getAll(SupportSQLiteQuery query); - public LiveData> getAll(int profileId, String filter) { - return getAll(new SimpleSQLiteQuery("SELECT\n" + - "*\n" + - "FROM luckyNumbers\n" + - "LEFT JOIN metadata ON luckyNumberDate = thingId AND thingType = "+TYPE_LUCKY_NUMBER+" AND metadata.profileId = "+profileId+"\n" + - "WHERE luckyNumbers.profileId = "+profileId+" AND "+filter+"\n" + - "ORDER BY addedDate DESC")); - } - public LiveData> getAll(int profileId) { - return getAll(profileId, "1"); - } - public LiveData> getAllWhere(int profileId, String filter) { - return getAll(profileId, filter); - } - - @RawQuery(observedEntities = {Notice.class, Metadata.class}) - abstract List getAllNow(SupportSQLiteQuery query); - public List getAllNow(int profileId, String filter) { - return getAllNow(new SimpleSQLiteQuery("SELECT\n" + - "*\n" + - "FROM luckyNumbers\n" + - "LEFT JOIN metadata ON luckyNumberDate = thingId AND thingType = "+TYPE_LUCKY_NUMBER+" AND metadata.profileId = "+profileId+"\n" + - "WHERE luckyNumbers.profileId = "+profileId+" AND "+filter+"\n" + - "ORDER BY addedDate DESC")); - } - public List getNotNotifiedNow(int profileId) { - return getAllNow(profileId, "notified = 0"); - } - - @Query("SELECT * FROM luckyNumbers\n" + - "LEFT JOIN metadata ON luckyNumberDate = thingId AND thingType = "+TYPE_LUCKY_NUMBER+" AND metadata.profileId = luckyNumbers.profileId " + - "WHERE notified = 0 " + - "ORDER BY addedDate DESC") - public abstract List getNotNotifiedNow(); -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.kt new file mode 100644 index 00000000..ad44c0d3 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/LuckyNumberDao.kt @@ -0,0 +1,74 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-25. + */ +package pl.szczodrzynski.edziennik.data.db.dao + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Query +import androidx.room.RawQuery +import androidx.sqlite.db.SupportSQLiteQuery +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.annotation.SelectiveDao +import pl.szczodrzynski.edziennik.annotation.UpdateSelective +import pl.szczodrzynski.edziennik.data.db.AppDb +import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber +import pl.szczodrzynski.edziennik.data.db.entity.Metadata +import pl.szczodrzynski.edziennik.data.db.full.LuckyNumberFull +import pl.szczodrzynski.edziennik.utils.models.Date + +@Dao +@SelectiveDao(db = AppDb::class) +abstract class LuckyNumberDao : BaseDao { + companion object { + private const val QUERY = """ + SELECT + * + FROM luckyNumbers + LEFT JOIN metadata ON luckyNumberDate = thingId AND thingType = ${Metadata.TYPE_LUCKY_NUMBER} AND metadata.profileId = luckyNumbers.profileId + """ + + private const val ORDER_BY = """ORDER BY luckyNumberDate DESC""" + } + + private val selective by lazy { LuckyNumberDaoSelective(App.db) } + + @RawQuery(observedEntities = [LuckyNumber::class]) + abstract override fun getRaw(query: SupportSQLiteQuery): LiveData> + @RawQuery(observedEntities = [LuckyNumber::class]) + abstract override fun getOne(query: SupportSQLiteQuery): LiveData + + // SELECTIVE UPDATE + @UpdateSelective(primaryKeys = ["profileId", "luckyNumberDate"], skippedColumns = ["addedDate"]) + override fun update(item: LuckyNumber) = selective.update(item) + override fun updateAll(items: List) = selective.updateAll(items) + + // CLEAR + @Query("DELETE FROM luckyNumbers WHERE profileId = :profileId") + abstract override fun clear(profileId: Int) + // REMOVE NOT KEPT + @Query("DELETE FROM luckyNumbers WHERE keep = 0") + abstract override fun removeNotKept() + + // GET ALL - LIVE DATA + fun getAll(profileId: Int) = + getRaw("$QUERY WHERE luckyNumbers.profileId = $profileId $ORDER_BY") + + // GET ALL - NOW + fun getAllNow(profileId: Int) = + getRawNow("$QUERY WHERE luckyNumbers.profileId = $profileId $ORDER_BY") + fun getNotNotifiedNow() = + getRawNow("$QUERY WHERE notified = 0 $ORDER_BY") + fun getNotNotifiedNow(profileId: Int) = + getRawNow("$QUERY WHERE luckyNumbers.profileId = $profileId AND notified = 0 $ORDER_BY") + + // GET ONE - LIVE DATA + fun getNearestFuture(profileId: Int, today: Date) = + getOne("$QUERY WHERE luckyNumbers.profileId = $profileId AND luckyNumberDate >= ${today.value} $ORDER_BY LIMIT 1") + + // GET ONE - NOW + fun getByIdNow(profileId: Int, id: Long) = + getOneNow("$QUERY WHERE attendances.profileId = $profileId AND attendanceId = $id") + fun getNearestFutureNow(profileId: Int, today: Date) = + getOneNow("$QUERY WHERE luckyNumbers.profileId = $profileId AND luckyNumberDate >= ${today.value} $ORDER_BY LIMIT 1") +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MessageDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MessageDao.kt index 606f29a7..a0be36ab 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MessageDao.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MessageDao.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 + * Copyright (c) Kuba Szczodrzyński 2020-4-25. */ package pl.szczodrzynski.edziennik.data.db.dao @@ -36,7 +36,10 @@ abstract class MessageDao : BaseDao { @RawQuery(observedEntities = [Message::class]) abstract override fun getRaw(query: SupportSQLiteQuery): LiveData> + @RawQuery(observedEntities = [Message::class]) + abstract override fun getOne(query: SupportSQLiteQuery): LiveData + // SELECTIVE UPDATE @UpdateSelective(primaryKeys = ["profileId", "messageId"], skippedColumns = ["messageType", "messageBody", "messageIsPinned", "attachmentIds", "attachmentNames", "attachmentSizes"]) override fun update(item: Message) = selective.update(item) override fun updateAll(items: List) = selective.updateAll(items) @@ -44,6 +47,9 @@ abstract class MessageDao : BaseDao { // CLEAR @Query("DELETE FROM messages WHERE profileId = :profileId") abstract override fun clear(profileId: Int) + // REMOVE NOT KEPT + @Query("DELETE FROM messages WHERE keep = 0") + abstract override fun removeNotKept() // GET ALL - LIVE DATA fun getAll(profileId: Int) = @@ -64,4 +70,6 @@ abstract class MessageDao : BaseDao { // GET ONE - NOW fun getByIdNow(profileId: Int, id: Long) = getOneNow("$QUERY WHERE messages.profileId = $profileId AND messageId = $id") + + } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MetadataDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MetadataDao.java index 7a074bca..eac92f21 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MetadataDao.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/MetadataDao.java @@ -63,37 +63,37 @@ public abstract class MetadataDao { @Transaction public void setSeen(int profileId, Object o, boolean seen) { if (o instanceof Grade) { - if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), seen, false, 0)) == -1) { + if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), seen, false)) == -1) { updateSeen(profileId, TYPE_GRADE, ((Grade) o).getId(), seen); } } if (o instanceof Attendance) { - if (add(new Metadata(profileId, TYPE_ATTENDANCE, ((Attendance) o).id, seen, false, 0)) == -1) { - updateSeen(profileId, TYPE_ATTENDANCE, ((Attendance) o).id, seen); + if (add(new Metadata(profileId, TYPE_ATTENDANCE, ((Attendance) o).getId(), seen, false)) == -1) { + updateSeen(profileId, TYPE_ATTENDANCE, ((Attendance) o).getId(), seen); } } if (o instanceof Notice) { - if (add(new Metadata(profileId, TYPE_NOTICE, ((Notice) o).id, seen, false, 0)) == -1) { - updateSeen(profileId, TYPE_NOTICE, ((Notice) o).id, seen); + if (add(new Metadata(profileId, TYPE_NOTICE, ((Notice) o).getId(), seen, false)) == -1) { + updateSeen(profileId, TYPE_NOTICE, ((Notice) o).getId(), seen); } } if (o instanceof Event) { - if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), seen, false, 0)) == -1) { + if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), seen, false)) == -1) { updateSeen(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), seen); } } if (o instanceof LessonFull) { - if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), seen, false, 0)) == -1) { + if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), seen, false)) == -1) { updateSeen(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), seen); } } if (o instanceof Announcement) { - if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, seen, false, 0)) == -1) { - updateSeen(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, seen); + if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).getId(), seen, false)) == -1) { + updateSeen(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).getId(), seen); } } if (o instanceof Message) { - if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), seen, false, 0)) == -1) { + if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), seen, false)) == -1) { updateSeen(profileId, TYPE_MESSAGE, ((Message) o).getId(), seen); } } @@ -102,37 +102,37 @@ public abstract class MetadataDao { @Transaction public void setNotified(int profileId, Object o, boolean notified) { if (o instanceof Grade) { - if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), false, notified, 0)) == -1) { + if (add(new Metadata(profileId, TYPE_GRADE, ((Grade) o).getId(), false, notified)) == -1) { updateNotified(profileId, TYPE_GRADE, ((Grade) o).getId(), notified); } } if (o instanceof Attendance) { - if (add(new Metadata(profileId, TYPE_ATTENDANCE, ((Attendance) o).id, false, notified, 0)) == -1) { - updateNotified(profileId, TYPE_ATTENDANCE, ((Attendance) o).id, notified); + if (add(new Metadata(profileId, TYPE_ATTENDANCE, ((Attendance) o).getId(), false, notified)) == -1) { + updateNotified(profileId, TYPE_ATTENDANCE, ((Attendance) o).getId(), notified); } } if (o instanceof Notice) { - if (add(new Metadata(profileId, TYPE_NOTICE, ((Notice) o).id, false, notified, 0)) == -1) { - updateNotified(profileId, TYPE_NOTICE, ((Notice) o).id, notified); + if (add(new Metadata(profileId, TYPE_NOTICE, ((Notice) o).getId(), false, notified)) == -1) { + updateNotified(profileId, TYPE_NOTICE, ((Notice) o).getId(), notified); } } if (o instanceof Event) { - if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), false, notified, 0)) == -1) { + if (add(new Metadata(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), false, notified)) == -1) { updateNotified(profileId, ((Event) o).getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, ((Event) o).getId(), notified); } } if (o instanceof LessonFull) { - if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), false, notified, 0)) == -1) { + if (add(new Metadata(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), false, notified)) == -1) { updateNotified(profileId, TYPE_LESSON_CHANGE, ((LessonFull) o).getId(), notified); } } if (o instanceof Announcement) { - if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, false, notified, 0)) == -1) { - updateNotified(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).id, notified); + if (add(new Metadata(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).getId(), false, notified)) == -1) { + updateNotified(profileId, TYPE_ANNOUNCEMENT, ((Announcement) o).getId(), notified); } } if (o instanceof Message) { - if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), false, notified, 0)) == -1) { + if (add(new Metadata(profileId, TYPE_MESSAGE, ((Message) o).getId(), false, notified)) == -1) { updateNotified(profileId, TYPE_MESSAGE, ((Message) o).getId(), notified); } } @@ -141,7 +141,7 @@ public abstract class MetadataDao { @Transaction public void setBoth(int profileId, Event o, boolean seen, boolean notified, long addedDate) { if (o != null) { - if (add(new Metadata(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), seen, notified, addedDate)) == -1) { + if (add(new Metadata(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), seen, notified)) == -1) { updateSeen(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), seen); updateNotified(profileId, o.getType() == Event.TYPE_HOMEWORK ? TYPE_HOMEWORK : TYPE_EVENT, o.getId(), notified); } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.java deleted file mode 100644 index 110544e4..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.dao; - -import androidx.lifecycle.LiveData; -import androidx.room.Dao; -import androidx.room.Insert; -import androidx.room.OnConflictStrategy; -import androidx.room.Query; -import androidx.room.RawQuery; -import androidx.sqlite.db.SimpleSQLiteQuery; -import androidx.sqlite.db.SupportSQLiteQuery; - -import java.util.List; - -import pl.szczodrzynski.edziennik.data.db.entity.Metadata; -import pl.szczodrzynski.edziennik.data.db.entity.Notice; -import pl.szczodrzynski.edziennik.data.db.full.NoticeFull; - -import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_NOTICE; - -@Dao -public abstract class NoticeDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - public abstract long add(Notice notice); - - @Insert(onConflict = OnConflictStrategy.REPLACE) - public abstract void addAll(List noticeList); - - @Query("DELETE FROM notices WHERE profileId = :profileId") - public abstract void clear(int profileId); - - @Query("DELETE FROM notices WHERE profileId = :profileId AND noticeSemester = :semester") - public abstract void clearForSemester(int profileId, int semester); - - @RawQuery(observedEntities = {Notice.class}) - abstract LiveData> getAll(SupportSQLiteQuery query); - public LiveData> getAll(int profileId, String filter) { - return getAll(new SimpleSQLiteQuery("SELECT \n" + - "*, \n" + - "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" + - "FROM notices \n" + - "LEFT JOIN teachers USING(profileId, teacherId)\n" + - "LEFT JOIN metadata ON noticeId = thingId AND thingType = "+TYPE_NOTICE+" AND metadata.profileId = "+profileId+"\n" + - "WHERE notices.profileId = "+profileId+" AND "+filter+"\n" + - "ORDER BY addedDate DESC")); - } - public LiveData> getAll(int profileId) { - return getAll(profileId, "1"); - } - public LiveData> getAllWhere(int profileId, String filter) { - return getAll(profileId, filter); - } - - @RawQuery(observedEntities = {Notice.class, Metadata.class}) - abstract List getAllNow(SupportSQLiteQuery query); - public List getAllNow(int profileId, String filter) { - return getAllNow(new SimpleSQLiteQuery("SELECT \n" + - "*, \n" + - "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName\n" + - "FROM notices \n" + - "LEFT JOIN teachers USING(profileId, teacherId)\n" + - "LEFT JOIN metadata ON noticeId = thingId AND thingType = "+TYPE_NOTICE+" AND metadata.profileId = "+profileId+"\n" + - "WHERE notices.profileId = "+profileId+" AND "+filter+"\n" + - "ORDER BY addedDate DESC")); - } - public List getNotNotifiedNow(int profileId) { - return getAllNow(profileId, "notified = 0"); - } - - @Query("SELECT " + - "*, " + - "teachers.teacherName || ' ' || teachers.teacherSurname AS teacherFullName " + - "FROM notices " + - "LEFT JOIN teachers USING(profileId, teacherId) " + - "LEFT JOIN metadata ON noticeId = thingId AND thingType = "+TYPE_NOTICE+" AND metadata.profileId = notices.profileId " + - "WHERE notified = 0 " + - "ORDER BY addedDate DESC") - public abstract List getNotNotifiedNow(); -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.kt new file mode 100644 index 00000000..79bae175 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/NoticeDao.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-25. + */ +package pl.szczodrzynski.edziennik.data.db.dao + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Query +import androidx.room.RawQuery +import androidx.sqlite.db.SupportSQLiteQuery +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.annotation.SelectiveDao +import pl.szczodrzynski.edziennik.annotation.UpdateSelective +import pl.szczodrzynski.edziennik.data.db.AppDb +import pl.szczodrzynski.edziennik.data.db.entity.Metadata +import pl.szczodrzynski.edziennik.data.db.entity.Notice +import pl.szczodrzynski.edziennik.data.db.full.NoticeFull + +@Dao +@SelectiveDao(db = AppDb::class) +abstract class NoticeDao : BaseDao { + companion object { + private const val QUERY = """ + SELECT + *, + teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName + FROM notices + LEFT JOIN teachers USING(profileId, teacherId) + LEFT JOIN metadata ON noticeId = thingId AND thingType = ${Metadata.TYPE_NOTICE} AND metadata.profileId = notices.profileId + """ + + private const val ORDER_BY = """ORDER BY addedDate DESC""" + } + + private val selective by lazy { NoticeDaoSelective(App.db) } + + @RawQuery(observedEntities = [Notice::class]) + abstract override fun getRaw(query: SupportSQLiteQuery): LiveData> + @RawQuery(observedEntities = [Notice::class]) + abstract override fun getOne(query: SupportSQLiteQuery): LiveData + + // SELECTIVE UPDATE + @UpdateSelective(primaryKeys = ["profileId", "noticeId"], skippedColumns = ["addedDate"]) + override fun update(item: Notice) = selective.update(item) + override fun updateAll(items: List) = selective.updateAll(items) + + // CLEAR + @Query("DELETE FROM notices WHERE profileId = :profileId") + abstract override fun clear(profileId: Int) + // REMOVE NOT KEPT + @Query("DELETE FROM notices WHERE keep = 0") + abstract override fun removeNotKept() + + // GET ALL - LIVE DATA + fun getAll(profileId: Int) = + getRaw("$QUERY WHERE notices.profileId = $profileId $ORDER_BY") + + // GET ALL - NOW + fun getAllNow(profileId: Int) = + getRawNow("$QUERY WHERE notices.profileId = $profileId $ORDER_BY") + fun getNotNotifiedNow() = + getRawNow("$QUERY WHERE notified = 0 $ORDER_BY") + fun getNotNotifiedNow(profileId: Int) = + getRawNow("$QUERY WHERE notices.profileId = $profileId AND notified = 0 $ORDER_BY") + + // GET ONE - NOW + fun getByIdNow(profileId: Int, id: Long) = + getOneNow("$QUERY WHERE notices.profileId = $profileId AND noticeId = $id") + + @Query("UPDATE notices SET keep = 0 WHERE profileId = :profileId AND noticeSemester = :semester") + abstract fun dontKeepSemester(profileId: Int, semester: Int) +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TeacherAbsenceDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TeacherAbsenceDao.kt index 1d4881bb..1528adf1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TeacherAbsenceDao.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TeacherAbsenceDao.kt @@ -1,65 +1,74 @@ /* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 + * Copyright (c) Kuba Szczodrzyński 2020-4-25. */ - package pl.szczodrzynski.edziennik.data.db.dao import androidx.lifecycle.LiveData import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.RawQuery +import androidx.sqlite.db.SupportSQLiteQuery +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.annotation.SelectiveDao +import pl.szczodrzynski.edziennik.annotation.UpdateSelective +import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.entity.TeacherAbsence import pl.szczodrzynski.edziennik.data.db.full.TeacherAbsenceFull import pl.szczodrzynski.edziennik.utils.models.Date @Dao -interface TeacherAbsenceDao { +@SelectiveDao(db = AppDb::class) +abstract class TeacherAbsenceDao : BaseDao { + companion object { + private const val QUERY = """ + SELECT + *, + teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName + FROM teacherAbsence + LEFT JOIN teachers USING(profileId, teacherId) + LEFT JOIN metadata ON teacherAbsenceId = thingId AND thingType = ${Metadata.TYPE_TEACHER_ABSENCE} AND metadata.profileId = teacherAbsence.profileId + """ - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun add(teacherAbsence: TeacherAbsence) + private const val ORDER_BY = """ORDER BY teacherAbsenceDateFrom ASC""" + } - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun addAll(teacherAbsenceList: List) + private val selective by lazy { TeacherAbsenceDaoSelective(App.db) } - @Query("SELECT * FROM teacherAbsence WHERE profileId = :profileId") - fun getAll(profileId: Int): List + @RawQuery(observedEntities = [TeacherAbsence::class]) + abstract override fun getRaw(query: SupportSQLiteQuery): LiveData> + @RawQuery(observedEntities = [TeacherAbsence::class]) + abstract override fun getOne(query: SupportSQLiteQuery): LiveData - @Query("SELECT *, teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName, " + - "metadata.seen, metadata.notified, metadata.addedDate FROM teacherAbsence " + - "LEFT JOIN teachers USING (profileId, teacherId) " + - "LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = " + Metadata.TYPE_TEACHER_ABSENCE + - " AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId") - fun getAllFullNow(profileId: Int): List - - @Query("SELECT *, teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName, " + - "metadata.seen, metadata.notified, metadata.addedDate FROM teacherAbsence " + - "LEFT JOIN teachers USING (profileId, teacherId) " + - "LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = " + Metadata.TYPE_TEACHER_ABSENCE + - " AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId " + - "AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo") - fun getAllByDateFull(profileId: Int, date: Date): LiveData> - - @Query("SELECT *, teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName, " + - "metadata.seen, metadata.notified, metadata.addedDate FROM teacherAbsence " + - "LEFT JOIN teachers USING (profileId, teacherId) " + - "LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = " + Metadata.TYPE_TEACHER_ABSENCE + - " AND metadata.profileId = :profileId WHERE teachers.profileId = :profileId " + - "AND :date BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo") - fun getAllByDateNow(profileId: Int, date: Date): List - - @Query(""" - SELECT *, - teachers.teacherName || ' ' || teachers.teacherSurname as teacherFullName - FROM teacherAbsence - LEFT JOIN teachers USING (profileId, teacherId) - LEFT JOIN metadata ON teacherAbsenceId = thingId AND metadata.thingType = ${Metadata.TYPE_TEACHER_ABSENCE} - AND teachers.profileId = metadata.profileId WHERE metadata.notified = 0 - ORDER BY addedDate DESC - """) - fun getNotNotifiedNow(): List + // SELECTIVE UPDATE + @UpdateSelective(primaryKeys = ["profileId", "teacherAbsenceId"], skippedColumns = ["addedDate"]) + override fun update(item: TeacherAbsence) = selective.update(item) + override fun updateAll(items: List) = selective.updateAll(items) + // CLEAR @Query("DELETE FROM teacherAbsence WHERE profileId = :profileId") - fun clear(profileId: Int) + abstract override fun clear(profileId: Int) + // REMOVE NOT KEPT + @Query("DELETE FROM teacherAbsence WHERE keep = 0") + abstract override fun removeNotKept() + + // GET ALL - LIVE DATA + fun getAll(profileId: Int) = + getRaw("$QUERY WHERE teacherAbsence.profileId = $profileId $ORDER_BY") + fun getAllByDate(profileId: Int, date: Date) = + getRaw("$QUERY WHERE teacherAbsence.profileId = $profileId AND '${date.stringY_m_d}' BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo $ORDER_BY") + + // GET ALL - NOW + fun getAllNow(profileId: Int) = + getRawNow("$QUERY WHERE teacherAbsence.profileId = $profileId $ORDER_BY") + fun getNotNotifiedNow() = + getRawNow("$QUERY WHERE notified = 0 $ORDER_BY") + fun getNotNotifiedNow(profileId: Int) = + getRawNow("$QUERY WHERE teacherAbsence.profileId = $profileId AND notified = 0 $ORDER_BY") + fun getAllByDateNow(profileId: Int, date: Date) = + getRawNow("$QUERY WHERE teacherAbsence.profileId = $profileId AND '${date.stringY_m_d}' BETWEEN teacherAbsenceDateFrom AND teacherAbsenceDateTo $ORDER_BY") + + // GET ONE - NOW + fun getByIdNow(profileId: Int, id: Long) = + getOneNow("$QUERY WHERE teacherAbsence.profileId = $profileId AND teacherAbsenceId = $id") } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TimetableDao.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TimetableDao.kt index 9ac9709b..cc236dc6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TimetableDao.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/dao/TimetableDao.kt @@ -1,20 +1,25 @@ /* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 + * Copyright (c) Kuba Szczodrzyński 2020-4-25. */ - package pl.szczodrzynski.edziennik.data.db.dao import androidx.lifecycle.LiveData -import androidx.room.* -import androidx.sqlite.db.SimpleSQLiteQuery +import androidx.room.Dao +import androidx.room.Query +import androidx.room.RawQuery import androidx.sqlite.db.SupportSQLiteQuery +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.annotation.SelectiveDao +import pl.szczodrzynski.edziennik.annotation.UpdateSelective +import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.data.db.full.LessonFull import pl.szczodrzynski.edziennik.utils.models.Date @Dao -interface TimetableDao { +@SelectiveDao(db = AppDb::class) +abstract class TimetableDao : BaseDao { companion object { private const val QUERY = """ SELECT @@ -25,7 +30,7 @@ interface TimetableDao { oldS.subjectLongName AS oldSubjectName, oldT.teacherName ||" "|| oldT.teacherSurname AS oldTeacherName, oldG.teamName AS oldTeamName, - metadata.seen, metadata.notified, metadata.addedDate + metadata.seen, metadata.notified FROM timetable LEFT JOIN subjects USING(profileId, subjectId) LEFT JOIN teachers USING(profileId, teacherId) @@ -35,111 +40,77 @@ interface TimetableDao { LEFT JOIN teams AS oldG ON timetable.profileId = oldG.profileId AND timetable.oldTeamId = oldG.teamId LEFT JOIN metadata ON id = thingId AND thingType = ${Metadata.TYPE_LESSON_CHANGE} AND metadata.profileId = timetable.profileId """ + + private const val ORDER_BY = """ORDER BY profileId, id, type""" + private const val IS_CHANGED = """type != -1 AND type != 0""" } - @Insert(onConflict = OnConflictStrategy.REPLACE) - operator fun plusAssign(lessonList: List) - - @Query("DELETE FROM timetable WHERE profileId = :profileId") - fun clear(profileId: Int) - - @Query("DELETE FROM timetable WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date >= :dateFrom) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom))") - fun clearFromDate(profileId: Int, dateFrom: Date) - - @Query("DELETE FROM timetable WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate <= :dateTo))") - fun clearToDate(profileId: Int, dateTo: Date) - - @Query("DELETE FROM timetable WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo))") - fun clearBetweenDates(profileId: Int, dateFrom: Date, dateTo: Date) + private val selective by lazy { TimetableDaoSelective(App.db) } @RawQuery(observedEntities = [Lesson::class]) - fun getRaw(query: SupportSQLiteQuery): LiveData> + abstract override fun getRaw(query: SupportSQLiteQuery): LiveData> + @RawQuery(observedEntities = [Lesson::class]) + abstract override fun getOne(query: SupportSQLiteQuery): LiveData - @Query(""" - $QUERY - WHERE timetable.profileId = :profileId AND type != -1 AND type != 0 - ORDER BY id, type - """) - fun getAllChangesNow(profileId: Int): List + // SELECTIVE UPDATE + @UpdateSelective(primaryKeys = ["profileId", "id"], skippedColumns = ["addedDate"]) + override fun update(item: Lesson) = selective.update(item) + override fun updateAll(items: List) = selective.updateAll(items) - @Query(""" - $QUERY - WHERE timetable.profileId = :profileId AND type != -1 AND type != 0 AND ((type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date)) - ORDER BY id, type - """) - fun getChangesForDateNow(profileId: Int, date: Date): List + // CLEAR + @Query("DELETE FROM timetable WHERE profileId = :profileId") + abstract override fun clear(profileId: Int) + // REMOVE NOT KEPT + @Query("DELETE FROM timetable WHERE keep = 0") + abstract override fun removeNotKept() - fun getForDate(profileId: Int, date: Date) = getRaw(SimpleSQLiteQuery(""" - $QUERY - WHERE timetable.profileId = $profileId AND ((type != 3 AND date = "${date.stringY_m_d}") OR ((type = 3 OR type = 1) AND oldDate = "${date.stringY_m_d}")) - ORDER BY id, type - """)) + // GET ALL - LIVE DATA + fun getAll(profileId: Int) = + getRaw("$QUERY WHERE timetable.profileId = $profileId $ORDER_BY") + fun getAllForDate(profileId: Int, date: Date) = + getRaw("$QUERY WHERE timetable.profileId = $profileId AND ((type != 3 AND date = '${date.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate = '${date.stringY_m_d}')) $ORDER_BY") + fun getNextWithSubject(profileId: Int, date: Date, subjectId: Long) = + getOne("$QUERY " + + "WHERE timetable.profileId = $profileId " + + "AND ((type != 3 AND date > '${date.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate > '${date.stringY_m_d}')) " + + "AND timetable.subjectId = $subjectId " + + "LIMIT 1") + fun getNextWithSubjectAndTeam(profileId: Int, date: Date, subjectId: Long, teamId: Long) = + getOne("$QUERY " + + "WHERE timetable.profileId = $profileId " + + "AND ((type != 3 AND date > '${date.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate > '${date.stringY_m_d}')) " + + "AND timetable.subjectId = $subjectId " + + "AND timetable.teamId = $teamId " + + "LIMIT 1") + fun getBetweenDates(dateFrom: Date, dateTo: Date) = + getRaw("$QUERY WHERE (type != 3 AND date >= '${dateFrom.stringY_m_d}' AND date <= '${dateTo.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate >= '${dateFrom.stringY_m_d}' AND oldDate <= '${dateTo.stringY_m_d}') $ORDER_BY") - @Query(""" - $QUERY - WHERE timetable.profileId = :profileId AND ((type != 3 AND date = :date) OR ((type = 3 OR type = 1) AND oldDate = :date)) - ORDER BY id, type - """) - fun getForDateNow(profileId: Int, date: Date): List + // GET ALL - NOW + fun getAllNow(profileId: Int) = + getRawNow("$QUERY WHERE timetable.profileId = $profileId $ORDER_BY") + fun getNotNotifiedNow() = + getRawNow("$QUERY WHERE notified = 0 AND timetable.type NOT IN (${Lesson.TYPE_NORMAL}, ${Lesson.TYPE_NO_LESSONS}, ${Lesson.TYPE_SHIFTED_SOURCE}) $ORDER_BY") + fun getNotNotifiedNow(profileId: Int) = + getRawNow("$QUERY WHERE timetable.profileId = $profileId AND notified = 0 AND timetable.type NOT IN (${Lesson.TYPE_NORMAL}, ${Lesson.TYPE_NO_LESSONS}, ${Lesson.TYPE_SHIFTED_SOURCE}) $ORDER_BY") + fun getAllForDateNow(profileId: Int, date: Date) = + getRawNow("$QUERY WHERE timetable.profileId = $profileId AND ((type != 3 AND date = '${date.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate = '${date.stringY_m_d}')) $ORDER_BY") + fun getChangesNow(profileId: Int) = + getRawNow("$QUERY WHERE timetable.profileId = $profileId AND $IS_CHANGED $ORDER_BY") + fun getChangesForDateNow(profileId: Int, date: Date) = + getRawNow("$QUERY WHERE timetable.profileId = $profileId AND $IS_CHANGED AND ((type != 3 AND date = '${date.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate = '${date.stringY_m_d}')) $ORDER_BY") + fun getBetweenDatesNow(dateFrom: Date, dateTo: Date) = + getRawNow("$QUERY WHERE (type != 3 AND date >= '${dateFrom.stringY_m_d}' AND date <= '${dateTo.stringY_m_d}') OR ((type = 3 OR type = 1) AND oldDate >= '${dateFrom.stringY_m_d}' AND oldDate <= '${dateTo.stringY_m_d}') $ORDER_BY") - @Query(""" - $QUERY - WHERE timetable.profileId = :profileId AND ((type != 3 AND date > :today) OR ((type = 3 OR type = 1) AND oldDate > :today)) AND timetable.subjectId = :subjectId - ORDER BY id, type - LIMIT 1 - """) - fun getNextWithSubject(profileId: Int, today: Date, subjectId: Long): LiveData + // GET ONE - NOW + fun getByIdNow(profileId: Int, id: Long) = + getOneNow("$QUERY WHERE timetable.profileId = $profileId AND timetable.id = $id") - @Query(""" - $QUERY - WHERE timetable.profileId = :profileId AND ((type != 3 AND date > :today) OR ((type = 3 OR type = 1) AND oldDate > :today)) AND timetable.subjectId = :subjectId AND timetable.teamId = :teamId - ORDER BY id, type - LIMIT 1 - """) - fun getNextWithSubjectAndTeam(profileId: Int, today: Date, subjectId: Long, teamId: Long): LiveData + @Query("UPDATE timetable SET keep = 0 WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date >= :dateFrom) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom))") + abstract fun dontKeepFromDate(profileId: Int, dateFrom: Date) - @Query(""" - $QUERY - WHERE (type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo) - ORDER BY profileId, id, type - """) - fun getBetweenDatesNow(dateFrom: Date, dateTo: Date): List + @Query("UPDATE timetable SET keep = 0 WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate <= :dateTo))") + abstract fun dontKeepToDate(profileId: Int, dateTo: Date) - @Query(""" - $QUERY - WHERE (type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo) - ORDER BY profileId, id, type - """) - fun getBetweenDates(dateFrom: Date, dateTo: Date): LiveData> - - @Query(""" - $QUERY - WHERE timetable.profileId = :profileId AND timetable.id = :lessonId - ORDER BY id, type - """) - fun getByIdNow(profileId: Int, lessonId: Long): LessonFull? - - @Query(""" - $QUERY - WHERE timetable.profileId = :profileId AND timetable.type NOT IN (${Lesson.TYPE_NORMAL}, ${Lesson.TYPE_NO_LESSONS}, ${Lesson.TYPE_SHIFTED_SOURCE}) AND metadata.notified = 0 - """) - fun getNotNotifiedNow(profileId: Int): List - - @Query(""" - SELECT - timetable.*, - subjects.subjectLongName AS subjectName, - teachers.teacherName ||" "|| teachers.teacherSurname AS teacherName, - oldS.subjectLongName AS oldSubjectName, - oldT.teacherName ||" "|| oldT.teacherSurname AS oldTeacherName, - metadata.seen, metadata.notified, metadata.addedDate - FROM timetable - LEFT JOIN subjects USING(profileId, subjectId) - LEFT JOIN teachers USING(profileId, teacherId) - LEFT JOIN subjects AS oldS ON timetable.profileId = oldS.profileId AND timetable.oldSubjectId = oldS.subjectId - LEFT JOIN teachers AS oldT ON timetable.profileId = oldT.profileId AND timetable.oldTeacherId = oldT.teacherId - LEFT JOIN metadata ON id = thingId AND thingType = ${Metadata.TYPE_LESSON_CHANGE} AND metadata.profileId = timetable.profileId - WHERE timetable.type NOT IN (${Lesson.TYPE_NORMAL}, ${Lesson.TYPE_NO_LESSONS}, ${Lesson.TYPE_SHIFTED_SOURCE}) AND metadata.notified = 0 - """) - fun getNotNotifiedNow(): List + @Query("UPDATE timetable SET keep = 0 WHERE profileId = :profileId AND type != -1 AND ((type != 3 AND date >= :dateFrom AND date <= :dateTo) OR ((type = 3 OR type = 1) AND oldDate >= :dateFrom AND oldDate <= :dateTo))") + abstract fun dontKeepBetweenDates(profileId: Int, dateFrom: Date, dateTo: Date) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.java deleted file mode 100644 index af17e0c3..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.entity; - -import androidx.annotation.Nullable; -import androidx.room.ColumnInfo; -import androidx.room.Entity; -import androidx.room.Ignore; -import androidx.room.Index; - -import pl.szczodrzynski.edziennik.utils.models.Date; - -@Entity(tableName = "announcements", - primaryKeys = {"profileId", "announcementId"}, - indices = {@Index(value = {"profileId"})}) -public class Announcement { - public int profileId; - - @ColumnInfo(name = "announcementId") - public long id; - - @ColumnInfo(name = "announcementSubject") - public String subject; - @Nullable - @ColumnInfo(name = "announcementText") - public String text; - @Nullable - @ColumnInfo(name = "announcementStartDate") - public Date startDate; - @Nullable - @ColumnInfo(name = "announcementEndDate") - public Date endDate; - - public long teacherId; - - @Nullable - @ColumnInfo(name = "announcementIdString") - public String idString; - - @Ignore - public Announcement() {} - - public Announcement(int profileId, long id, String subject, String text, @Nullable Date startDate, @Nullable Date endDate, long teacherId, @Nullable String idString) { - this.profileId = profileId; - this.id = id; - this.subject = subject; - this.text = text; - this.startDate = startDate; - this.endDate = endDate; - this.teacherId = teacherId; - this.idString = idString; - } -} - - - diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.kt new file mode 100644 index 00000000..82f7bb78 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Announcement.kt @@ -0,0 +1,40 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-24. + */ +package pl.szczodrzynski.edziennik.data.db.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.Index +import pl.szczodrzynski.edziennik.utils.models.Date + +@Entity(tableName = "announcements", + primaryKeys = ["profileId", "announcementId"], + indices = [ + Index(value = ["profileId"]) + ]) +open class Announcement( + val profileId: Int, + @ColumnInfo(name = "announcementId") + var id: Long, + @ColumnInfo(name = "announcementSubject") + var subject: String, + @ColumnInfo(name = "announcementText") + var text: String?, + + @ColumnInfo(name = "announcementStartDate") + var startDate: Date?, + @ColumnInfo(name = "announcementEndDate") + var endDate: Date?, + + var teacherId: Long, + var addedDate: Long = System.currentTimeMillis() +) : Keepable() { + + @ColumnInfo(name = "announcementIdString") + var idString: String? = null + + @Ignore + var showAsUnseen: Boolean? = null +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.java deleted file mode 100644 index 039dcb0c..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.entity; - -import androidx.annotation.NonNull; -import androidx.room.ColumnInfo; -import androidx.room.Entity; -import androidx.room.Ignore; -import androidx.room.Index; - -import pl.szczodrzynski.edziennik.utils.models.Date; -import pl.szczodrzynski.edziennik.utils.models.Time; - -@Entity(tableName = "attendances", - primaryKeys = {"profileId", "attendanceId", "attendanceLessonDate", "attendanceStartTime"}, - indices = {@Index(value = {"profileId"})}) -public class Attendance { - public int profileId; - - @ColumnInfo(name = "attendanceId") - public long id; - - @NonNull - @ColumnInfo(name = "attendanceLessonDate") - public Date lessonDate; - @NonNull - @ColumnInfo(name = "attendanceStartTime") - public Time startTime; - @ColumnInfo(name = "attendanceLessonTopic") - public String lessonTopic; - @ColumnInfo(name = "attendanceSemester") - public int semester; - public static final int TYPE_PRESENT = 0; - public static final int TYPE_ABSENT = 1; - public static final int TYPE_ABSENT_EXCUSED = 2; - public static final int TYPE_RELEASED = 3; - public static final int TYPE_BELATED = 4; - public static final int TYPE_BELATED_EXCUSED = 5; - public static final int TYPE_CUSTOM = 6; - public static final int TYPE_DAY_FREE = 7; - @ColumnInfo(name = "attendanceType") - public int type = TYPE_PRESENT; - - public long teacherId; - public long subjectId; - - @Ignore - public Attendance() { - this(-1, -1, -1, -1, 0, "", Date.getToday(), Time.getNow(), TYPE_PRESENT); - } - - public Attendance(int profileId, long id, long teacherId, long subjectId, int semester, String lessonTopic, Date lessonDate, Time startTime, int type) { - this.profileId = profileId; - this.id = id; - this.teacherId = teacherId; - this.subjectId = subjectId; - this.semester = semester; - this.lessonTopic = lessonTopic; - this.lessonDate = lessonDate; - this.startTime = startTime; - this.type = type; - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.kt new file mode 100644 index 00000000..b2ebb5a0 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.kt @@ -0,0 +1,78 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-24. + */ +package pl.szczodrzynski.edziennik.data.db.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.Index +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time + +@Entity(tableName = "attendances", + primaryKeys = ["profileId", "attendanceId"], + indices = [ + Index(value = ["profileId"]) + ]) +open class Attendance( + val profileId: Int, + @ColumnInfo(name = "attendanceId") + var id: Long, + /** Base type ID used to count attendance stats */ + @ColumnInfo(name = "attendanceBaseType") + var baseType: Int, + /** A full type name coming from the e-register */ + @ColumnInfo(name = "attendanceTypeName") + var typeName: String, + /** A short name to display by default, might be different for non-standard types */ + @ColumnInfo(name = "attendanceTypeShort") + val typeShort: String, + /** A short name that the e-register would display */ + @ColumnInfo(name = "attendanceTypeSymbol") + var typeSymbol: String, + /** A color that the e-register would display, null falls back to app's default */ + @ColumnInfo(name = "attendanceTypeColor") + var typeColor: Int?, + + @ColumnInfo(name = "attendanceDate") + var date: Date, + @ColumnInfo(name = "attendanceTime") + var startTime: Time?, + @ColumnInfo(name = "attendanceSemester") + var semester: Int, + + var teacherId: Long, + var subjectId: Long, + var addedDate: Long = System.currentTimeMillis() +) : Keepable() { + companion object { + const val TYPE_UNKNOWN = -1 // #3f51b5 + const val TYPE_PRESENT = 0 // #009688 + const val TYPE_PRESENT_CUSTOM = 10 // count as presence AND show in the list + custom color, fallback: #3f51b5 + const val TYPE_ABSENT = 1 // #ff3d00 + const val TYPE_ABSENT_EXCUSED = 2 // #76ff03 + const val TYPE_RELEASED = 3 // #9e9e9e + const val TYPE_BELATED = 4 // #ffc107 + const val TYPE_BELATED_EXCUSED = 5 // #ffc107 + const val TYPE_DAY_FREE = 6 // #43a047 + + // attendance bar order: + // day_free, present, present_custom, unknown, belated_excused, belated, released, absent_excused, absent, + } + + @ColumnInfo(name = "attendanceLessonTopic") + var lessonTopic: String? = null + @ColumnInfo(name = "attendanceLessonNumber") + var lessonNumber: Int? = null + @ColumnInfo(name = "attendanceIsCounted") + var isCounted: Boolean = true + + @Ignore + var showAsUnseen: Boolean? = null + + @delegate:Ignore + val typeObject by lazy { + AttendanceType(profileId, baseType.toLong(), baseType, typeName, typeShort, typeSymbol, typeColor).also { it.isCounted = isCounted } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt index 0e3e86f9..c5fccc4b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt @@ -5,19 +5,55 @@ package pl.szczodrzynski.edziennik.data.db.entity import androidx.room.Entity +import androidx.room.Ignore @Entity(tableName = "attendanceTypes", primaryKeys = ["profileId", "id"]) data class AttendanceType ( - val profileId: Int, - val id: Long, + /** Base type ID used to count attendance stats */ + val baseType: Int, + /** A full type name coming from the e-register */ + val typeName: String, + /** A short name to display by default, might be different for non-standard types */ + val typeShort: String, + /** A short name that the e-register would display */ + val typeSymbol: String, + /** A color that the e-register would display, null falls back to app's default */ + val typeColor: Int? +) : Comparable { - val name: String, + @Ignore + var isCounted: Boolean = true - val type: Int, - - val color: Int - -) + // attendance bar order: + // day_free, present, present_custom, unknown, belated_excused, belated, released, absent_excused, absent, + override fun compareTo(other: AttendanceType): Int { + val type1 = when (baseType) { + Attendance.TYPE_DAY_FREE -> 0 + Attendance.TYPE_PRESENT -> 1 + Attendance.TYPE_PRESENT_CUSTOM -> 2 + Attendance.TYPE_UNKNOWN -> 3 + Attendance.TYPE_BELATED_EXCUSED -> 4 + Attendance.TYPE_BELATED -> 5 + Attendance.TYPE_RELEASED -> 6 + Attendance.TYPE_ABSENT_EXCUSED -> 7 + Attendance.TYPE_ABSENT -> 8 + else -> 9 + } + val type2 = when (other.baseType) { + Attendance.TYPE_DAY_FREE -> 0 + Attendance.TYPE_PRESENT -> 1 + Attendance.TYPE_PRESENT_CUSTOM -> 2 + Attendance.TYPE_UNKNOWN -> 3 + Attendance.TYPE_BELATED_EXCUSED -> 4 + Attendance.TYPE_BELATED -> 5 + Attendance.TYPE_RELEASED -> 6 + Attendance.TYPE_ABSENT_EXCUSED -> 7 + Attendance.TYPE_ABSENT -> 8 + else -> 9 + } + return type1 - type2 + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt index b811ff61..9d13e883 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Event.kt @@ -41,7 +41,8 @@ open class Event( var teacherId: Long, var subjectId: Long, - var teamId: Long + var teamId: Long, + var addedDate: Long = System.currentTimeMillis() ) : Keepable() { companion object { const val TYPE_UNDEFINED = -2L diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt index 393ab332..5bf3bd86 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 + * Copyright (c) Kuba Szczodrzyński 2020-4-24. */ package pl.szczodrzynski.edziennik.data.db.entity @@ -8,23 +8,11 @@ import androidx.room.Entity import androidx.room.Ignore import androidx.room.Index -/*public Grade(int profileId, long id, String category, int color, String description, String name, float value, float weight, int semester, long teacherId, long subjectId) { - this.profileId = profileId; - this.id = id; - this.category = category; - this.color = color; - this.description = description; - this.name = name; - this.value = value; - this.weight = weight; - this.semester = semester; - this.teacherId = teacherId; - this.subjectId = subjectId; - }*/ - @Entity(tableName = "grades", primaryKeys = ["profileId", "gradeId"], - indices = [Index(value = ["profileId"])]) + indices = [ + Index(value = ["profileId"]) + ]) open class Grade( val profileId: Int, @ColumnInfo(name = "gradeId") @@ -40,6 +28,7 @@ open class Grade( var weight: Float, @ColumnInfo(name = "gradeColor") var color: Int, + @ColumnInfo(name = "gradeCategory") var category: String?, @ColumnInfo(name = "gradeDescription") @@ -50,8 +39,9 @@ open class Grade( @ColumnInfo(name = "gradeSemester") val semester: Int, val teacherId: Long, - val subjectId: Long -) { + val subjectId: Long, + var addedDate: Long = System.currentTimeMillis() +) : Keepable() { companion object { const val TYPE_NORMAL = 0 const val TYPE_SEMESTER1_PROPOSED = 1 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Lesson.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Lesson.kt index ceb6cf9c..0c97ba24 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Lesson.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Lesson.kt @@ -1,7 +1,6 @@ /* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 + * Copyright (c) Kuba Szczodrzyński 2020-4-25. */ - package pl.szczodrzynski.edziennik.data.db.entity import androidx.room.Entity @@ -11,12 +10,15 @@ import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time @Entity(tableName = "timetable", + primaryKeys = ["profileId", "id"], indices = [ Index(value = ["profileId", "type", "date"]), Index(value = ["profileId", "type", "oldDate"]) - ], - primaryKeys = ["profileId", "id"]) -open class Lesson(val profileId: Int, var id: Long) { + ]) +open class Lesson( + val profileId: Int, + var id: Long +) : Keepable() { companion object { const val TYPE_NO_LESSONS = -1 const val TYPE_NORMAL = 0 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.java deleted file mode 100644 index bca49ec5..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.entity; - -import androidx.annotation.NonNull; -import androidx.room.ColumnInfo; -import androidx.room.Entity; -import androidx.room.Ignore; - -import pl.szczodrzynski.edziennik.utils.models.Date; - -@Entity(tableName = "luckyNumbers", - primaryKeys = {"profileId", "luckyNumberDate"}) -public class LuckyNumber { - public int profileId; - - @NonNull - @ColumnInfo(name = "luckyNumberDate", typeAffinity = 3) - public Date date; - @ColumnInfo(name = "luckyNumber") - public int number; - - public LuckyNumber(int profileId, @NonNull Date date, int number) { - this.profileId = profileId; - this.date = date; - this.number = number; - } - - @Ignore - public LuckyNumber() { - this.date = Date.getToday(); - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.kt new file mode 100644 index 00000000..6fb943c9 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/LuckyNumber.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-24. + */ +package pl.szczodrzynski.edziennik.data.db.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Ignore +import pl.szczodrzynski.edziennik.utils.models.Date + +@Entity(tableName = "luckyNumbers", + primaryKeys = ["profileId", "luckyNumberDate"]) +open class LuckyNumber( + val profileId: Int, + @ColumnInfo(name = "luckyNumberDate", typeAffinity = ColumnInfo.INTEGER) + var date: Date, + @ColumnInfo(name = "luckyNumber") + var number: Int +) : Keepable() { + @Ignore + var showAsUnseen = false +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Message.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Message.kt index 1cf9b3ba..98105057 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Message.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Message.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 + * Copyright (c) Kuba Szczodrzyński 2020-4-25. */ package pl.szczodrzynski.edziennik.data.db.entity @@ -30,7 +30,8 @@ open class Message( * Keep in mind that this being null does NOT * necessarily mean the message is sent. */ - var senderId: Long? + var senderId: Long?, + var addedDate: Long = System.currentTimeMillis() ) : Keepable() { companion object { const val TYPE_RECEIVED = 0 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Metadata.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Metadata.java index 6ea94f4e..7374d915 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Metadata.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Metadata.java @@ -36,7 +36,6 @@ public class Metadata { public boolean seen; public boolean notified; - public long addedDate; @Ignore public Metadata() { @@ -45,13 +44,12 @@ public class Metadata { this.notified = false; } - public Metadata(int profileId, int thingType, long thingId, boolean seen, boolean notified, long addedDate) { + public Metadata(int profileId, int thingType, long thingId, boolean seen, boolean notified) { this.profileId = profileId; this.thingType = thingType; this.thingId = thingId; this.seen = seen; this.notified = notified; - this.addedDate = addedDate; } public String thingType() { @@ -86,7 +84,6 @@ public class Metadata { ", thingId=" + thingId + ", seen=" + seen + ", notified=" + notified + - ", addedDate=" + addedDate + '}'; } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.java deleted file mode 100644 index 306e6f92..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.entity; - -import androidx.room.ColumnInfo; -import androidx.room.Entity; -import androidx.room.Ignore; -import androidx.room.Index; - -@Entity(tableName = "notices", - primaryKeys = {"profileId", "noticeId"}, - indices = {@Index(value = {"profileId"})}) -public class Notice { - public int profileId; - - @ColumnInfo(name = "noticeId") - public long id; - - @ColumnInfo(name = "noticeText") - public String text; - @ColumnInfo(name = "noticeSemester") - public int semester; - public static final int TYPE_NEUTRAL = 0; - public static final int TYPE_POSITIVE = 1; - public static final int TYPE_NEGATIVE = 2; - @ColumnInfo(name = "noticeType") - public int type = TYPE_NEUTRAL; - - public float points = 0; - public String category = null; - - public long teacherId; - - @Ignore - public Notice() {} - - public Notice(int profileId, long id, String text, int semester, int type, long teacherId) { - this.profileId = profileId; - this.id = id; - this.text = text; - this.semester = semester; - this.type = type; - this.teacherId = teacherId; - } -} - - diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.kt new file mode 100644 index 00000000..0e1f21fe --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Notice.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-25. + */ +package pl.szczodrzynski.edziennik.data.db.entity + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.Index + +@Entity(tableName = "notices", + primaryKeys = ["profileId", "noticeId"], + indices = [ + Index(value = ["profileId"]) + ]) +open class Notice( + val profileId: Int, + @ColumnInfo(name = "noticeId") + var id: Long, + @ColumnInfo(name = "noticeType") + var type: Int, + @ColumnInfo(name = "noticeSemester") + var semester: Int, + + @ColumnInfo(name = "noticeText") + var text: String, + @ColumnInfo(name = "noticeCategory") + var category: String?, + @ColumnInfo(name = "noticePoints") + var points: Float?, + + var teacherId: Long, + var addedDate: Long = System.currentTimeMillis() +) : Keepable() { + companion object { + const val TYPE_NEUTRAL = 0 + const val TYPE_POSITIVE = 1 + const val TYPE_NEGATIVE = 2 + } + + @Ignore + var showAsUnseen = false +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/TeacherAbsence.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/TeacherAbsence.kt index af037851..9ec5f828 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/TeacherAbsence.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/TeacherAbsence.kt @@ -1,37 +1,41 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-25. + */ package pl.szczodrzynski.edziennik.data.db.entity import androidx.room.ColumnInfo import androidx.room.Entity +import androidx.room.Ignore +import androidx.room.Index import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time @Entity(tableName = "teacherAbsence", - primaryKeys = ["profileId", "teacherAbsenceId"]) -open class TeacherAbsence ( + primaryKeys = ["profileId", "teacherAbsenceId"], + indices = [ + Index(value = ["profileId"]) + ]) +open class TeacherAbsence( + val profileId: Int, + @ColumnInfo(name = "teacherAbsenceId") + val id: Long, + @ColumnInfo(name = "teacherAbsenceType") + val type: Long, + @ColumnInfo(name = "teacherAbsenceName") + val name: String?, - val profileId: Int, + @ColumnInfo(name = "teacherAbsenceDateFrom") + val dateFrom: Date, + @ColumnInfo(name = "teacherAbsenceDateTo") + val dateTo: Date, + @ColumnInfo(name = "teacherAbsenceTimeFrom") + val timeFrom: Time?, + @ColumnInfo(name = "teacherAbsenceTimeTo") + val timeTo: Time?, - @ColumnInfo(name = "teacherAbsenceId") - val id: Long, - - val teacherId: Long, - - @ColumnInfo(name = "teacherAbsenceType") - val type: Long, - - @ColumnInfo(name = "teacherAbsenceName") - val name: String?, - - @ColumnInfo(name = "teacherAbsenceDateFrom") - val dateFrom: Date, - - @ColumnInfo(name = "teacherAbsenceDateTo") - val dateTo: Date, - - @ColumnInfo(name = "teacherAbsenceTimeFrom") - val timeFrom: Time?, - - @ColumnInfo(name = "teacherAbsenceTimeTo") - val timeTo: Time? - -) + val teacherId: Long, + var addedDate: Long = System.currentTimeMillis() +) : Keepable() { + @Ignore + var showAsUnseen = false +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.java deleted file mode 100644 index 2811a0c7..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.full; - -import pl.szczodrzynski.edziennik.data.db.entity.Announcement; - -public class AnnouncementFull extends Announcement { - public String teacherFullName = ""; - - // metadata - public boolean seen; - public boolean notified; - public long addedDate; -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.kt new file mode 100644 index 00000000..5b4cb711 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AnnouncementFull.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-25. + */ +package pl.szczodrzynski.edziennik.data.db.full + +import pl.szczodrzynski.edziennik.data.db.entity.Announcement +import pl.szczodrzynski.edziennik.utils.models.Date + +class AnnouncementFull( + profileId: Int, id: Long, + subject: String, text: String?, + startDate: Date?, endDate: Date?, + teacherId: Long, addedDate: Long = System.currentTimeMillis() +) : Announcement( + profileId, id, + subject, text, + startDate, endDate, + teacherId, addedDate +) { + var teacherName: String? = null + + // metadata + var seen = false + var notified = false +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.java deleted file mode 100644 index 605d06b2..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.full; - -import pl.szczodrzynski.edziennik.data.db.entity.Attendance; - -public class AttendanceFull extends Attendance { - public String teacherFullName = ""; - - public String subjectLongName = ""; - public String subjectShortName = ""; - - // metadata - public boolean seen; - public boolean notified; - public long addedDate; -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.kt new file mode 100644 index 00000000..f235fafd --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/AttendanceFull.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-24. + */ +package pl.szczodrzynski.edziennik.data.db.full + +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.utils.models.Date +import pl.szczodrzynski.edziennik.utils.models.Time + +class AttendanceFull( + profileId: Int, id: Long, + baseType: Int, typeName: String, typeShort: String, typeSymbol: String, typeColor: Int?, + date: Date, startTime: Time, semester: Int, + teacherId: Long, subjectId: Long, addedDate: Long = System.currentTimeMillis() +) : Attendance( + profileId, id, + baseType, typeName, typeShort, typeSymbol, typeColor, + date, startTime, semester, + teacherId, subjectId, addedDate +) { + var teacherName: String? = null + var subjectLongName: String? = null + var subjectShortName: String? = null + + // metadata + var seen = false + get() = field || baseType == TYPE_PRESENT + var notified = false +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt index a3dcc2cb..01e85395 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/EventFull.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 + * Copyright (c) Kuba Szczodrzyński 2020-4-25. */ package pl.szczodrzynski.edziennik.data.db.full @@ -11,16 +11,16 @@ import pl.szczodrzynski.edziennik.utils.models.Time class EventFull( profileId: Int, id: Long, date: Date, time: Time?, topic: String, color: Int?, type: Long, - teacherId: Long, subjectId: Long, teamId: Long + teacherId: Long, subjectId: Long, teamId: Long, addedDate: Long = System.currentTimeMillis() ) : Event( profileId, id, date, time, topic, color, type, - teacherId, subjectId, teamId + teacherId, subjectId, teamId, addedDate ) { constructor(event: Event, metadata: Metadata? = null) : this( event.profileId, event.id, event.date, event.time, event.topic, event.color, event.type, - event.teacherId, event.subjectId, event.teamId) { + event.teacherId, event.subjectId, event.teamId, event.addedDate) { event.let { addedManually = it.addedManually sharedBy = it.sharedBy @@ -33,7 +33,6 @@ class EventFull( metadata?.let { seen = it.seen notified = it.notified - addedDate = it.addedDate } } @@ -45,10 +44,10 @@ class EventFull( var subjectShortName: String? = null var teamName: String? = null var teamCode: String? = null + // metadata var seen = false var notified = false - var addedDate: Long = 0 val eventColor get() = color ?: typeColor ?: 0xff2196f3.toInt() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/GradeFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/GradeFull.kt index 8cc80974..f8d90c05 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/GradeFull.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/GradeFull.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 + * Copyright (c) Kuba Szczodrzyński 2020-4-24. */ package pl.szczodrzynski.edziennik.data.db.full @@ -9,18 +9,18 @@ class GradeFull( profileId: Int, id: Long, name: String, type: Int, value: Float, weight: Float, color: Int, category: String?, description: String?, comment: String?, - semester: Int, teacherId: Long, subjectId: Long + semester: Int, teacherId: Long, subjectId: Long, addedDate: Long = System.currentTimeMillis() ) : Grade( profileId, id, name, type, value, weight, color, category, description, comment, - semester, teacherId, subjectId + semester, teacherId, subjectId, addedDate ) { + var teacherName: String? = null var subjectLongName: String? = null var subjectShortName: String? = null - var teacherFullName: String? = null + // metadata var seen = false var notified = false - var addedDate: Long = 0 } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LessonFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LessonFull.kt index 5661a614..0e9542b6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LessonFull.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LessonFull.kt @@ -1,3 +1,6 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-25. + */ package pl.szczodrzynski.edziennik.data.db.full import android.content.Context @@ -5,7 +8,11 @@ import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.data.db.entity.Lesson import pl.szczodrzynski.edziennik.utils.models.Time -class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) { +class LessonFull( + profileId: Int, id: Long +) : Lesson( + profileId, id +) { var subjectName: String? = null var teacherName: String? = null var teamName: String? = null @@ -126,5 +133,4 @@ class LessonFull(profileId: Int, id: Long) : Lesson(profileId, id) { // metadata var seen: Boolean = false var notified: Boolean = false - var addedDate: Long = 0 } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.java deleted file mode 100644 index d73cb95f..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.full; - -import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber; - -public class LuckyNumberFull extends LuckyNumber { - // metadata - public boolean seen; - public boolean notified; - public long addedDate; -} - diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.kt new file mode 100644 index 00000000..bb24724a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/LuckyNumberFull.kt @@ -0,0 +1,19 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-25. + */ +package pl.szczodrzynski.edziennik.data.db.full + +import pl.szczodrzynski.edziennik.data.db.entity.LuckyNumber +import pl.szczodrzynski.edziennik.utils.models.Date + +class LuckyNumberFull( + profileId: Int, date: Date, + number: Int +) : LuckyNumber( + profileId, date, + number +) { + // metadata + var seen = false + var notified = false +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt index 5b873b7a..3fdcdd0d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/MessageFull.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 + * Copyright (c) Kuba Szczodrzyński 2020-4-25. */ package pl.szczodrzynski.edziennik.data.db.full @@ -10,10 +10,12 @@ import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient class MessageFull( profileId: Int, id: Long, type: Int, - subject: String, body: String?, senderId: Long? + subject: String, body: String?, senderId: Long?, + addedDate: Long = System.currentTimeMillis() ) : Message( profileId, id, type, - subject, body, senderId + subject, body, senderId, + addedDate ) { var senderName: String? = null @Relation(parentColumn = "messageId", entityColumn = "messageId", entity = MessageRecipient::class) @@ -33,5 +35,4 @@ class MessageFull( // metadata var seen = false var notified = false - var addedDate: Long = 0 } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.java b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.java deleted file mode 100644 index 372650fd..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.java +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) Kacper Ziubryniewicz 2020-1-6 - */ - -package pl.szczodrzynski.edziennik.data.db.full; - -import pl.szczodrzynski.edziennik.data.db.entity.Notice; - -public class NoticeFull extends Notice { - public String teacherFullName = ""; - - // metadata - public boolean seen; - public boolean notified; - public long addedDate; -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.kt new file mode 100644 index 00000000..25aee14d --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/NoticeFull.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-25. + */ +package pl.szczodrzynski.edziennik.data.db.full + +import pl.szczodrzynski.edziennik.data.db.entity.Notice + +class NoticeFull( + profileId: Int, id: Long, type: Int, semester: Int, + text: String, category: String?, points: Float?, + teacherId: Long, addedDate: Long = System.currentTimeMillis() +) : Notice( + profileId, id, type, semester, + text, category, points, + teacherId, addedDate +) { + var teacherName: String? = null + + // metadata + var seen = false + var notified = false +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/TeacherAbsenceFull.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/TeacherAbsenceFull.kt index e9d53a59..39a88e0f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/TeacherAbsenceFull.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/full/TeacherAbsenceFull.kt @@ -1,17 +1,24 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-25. + */ package pl.szczodrzynski.edziennik.data.db.full import pl.szczodrzynski.edziennik.data.db.entity.TeacherAbsence import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time -class TeacherAbsenceFull(profileId: Int, id: Long, teacherId: Long, type: Long, name: String?, - dateFrom: Date, dateTo: Date, timeFrom: Time?, timeTo: Time?) - : TeacherAbsence(profileId, id, teacherId, type, name, dateFrom, dateTo, timeFrom, timeTo) { - - var teacherFullName = "" +class TeacherAbsenceFull( + profileId: Int, id: Long, type: Long, name: String?, + dateFrom: Date, dateTo: Date, timeFrom: Time?, timeTo: Time?, + teacherId: Long, addedDate: Long = System.currentTimeMillis() +) : TeacherAbsence( + profileId, id, type, name, + dateFrom, dateTo, timeFrom, timeTo, + teacherId, addedDate +) { + var teacherName: String? = null // metadata var seen: Boolean = false var notified: Boolean = false - var addedDate: Long = 0 } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration86.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration86.kt new file mode 100644 index 00000000..add43d71 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration86.kt @@ -0,0 +1,178 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-29. + */ + +package pl.szczodrzynski.edziennik.data.db.migration + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration86 : Migration(85, 86) { + override fun migrate(database: SupportSQLiteDatabase) { + // Migrating some models, moving addedDate from metadata to entities + + // Announcements + database.execSQL("""ALTER TABLE announcements RENAME TO _announcements""") + database.execSQL("""CREATE TABLE `announcements` ( + `announcementIdString` TEXT, + `profileId` INTEGER NOT NULL, + `announcementId` INTEGER NOT NULL, + `announcementSubject` TEXT NOT NULL, + `announcementText` TEXT, + `announcementStartDate` TEXT, + `announcementEndDate` TEXT, + `teacherId` INTEGER NOT NULL, + `addedDate` INTEGER NOT NULL, + `keep` INTEGER NOT NULL, + PRIMARY KEY(`profileId`, `announcementId`) + )""") + database.execSQL("""DROP INDEX IF EXISTS index_announcements_profileId""") + database.execSQL("""CREATE INDEX `index_announcements_profileId` ON `announcements` (`profileId`)""") + database.execSQL("""REPLACE INTO announcements ( + announcementIdString, profileId, announcementId, announcementSubject, announcementText, announcementStartDate, announcementEndDate, teacherId, addedDate, keep + ) SELECT + announcementIdString, profileId, announcementId, IFNULL(announcementSubject, ""), announcementText, announcementStartDate, announcementEndDate, teacherId, 0, 1 + FROM _announcements""") + database.execSQL("""DROP TABLE _announcements""") + + // Attendance Types + database.execSQL("""ALTER TABLE attendanceTypes RENAME TO _attendanceTypes""") + database.execSQL("""CREATE TABLE `attendanceTypes` ( + `profileId` INTEGER NOT NULL, + `id` INTEGER NOT NULL, + `baseType` INTEGER NOT NULL, + `typeName` TEXT NOT NULL, + `typeShort` TEXT NOT NULL, + `typeSymbol` TEXT NOT NULL, + `typeColor` INTEGER, + PRIMARY KEY(`profileId`, `id`) + )""") + database.execSQL("""REPLACE INTO attendanceTypes ( + profileId, id, + baseType, + typeName, + typeShort, + typeSymbol, + typeColor + ) SELECT + profileId, id, + CASE WHEN id > 100 AND type = 0 THEN 10 ELSE type END, + name, + CASE type WHEN 0 THEN "ob" WHEN 1 THEN "nb" WHEN 2 THEN "u" WHEN 3 THEN "zw" WHEN 4 THEN "sp" WHEN 5 THEN "su" WHEN 6 THEN "w" ELSE "?" END, + CASE type WHEN 0 THEN "ob" WHEN 1 THEN "nb" WHEN 2 THEN "u" WHEN 3 THEN "zw" WHEN 4 THEN "sp" WHEN 5 THEN "su" WHEN 6 THEN "w" ELSE "?" END, + CASE color WHEN -1 THEN NULL ELSE color END + FROM _attendanceTypes""") + database.execSQL("""DROP TABLE _attendanceTypes""") + + // Attendance + database.execSQL("""ALTER TABLE attendances RENAME TO _attendances""") + database.execSQL("""CREATE TABLE `attendances` ( + `attendanceLessonTopic` TEXT, + `attendanceLessonNumber` INTEGER, + `profileId` INTEGER NOT NULL, + `attendanceId` INTEGER NOT NULL, + `attendanceBaseType` INTEGER NOT NULL, + `attendanceTypeName` TEXT NOT NULL, + `attendanceTypeShort` TEXT NOT NULL, + `attendanceTypeSymbol` TEXT NOT NULL, + `attendanceTypeColor` INTEGER, + `attendanceDate` TEXT NOT NULL, + `attendanceTime` TEXT, + `attendanceSemester` INTEGER NOT NULL, + `teacherId` INTEGER NOT NULL, + `subjectId` INTEGER NOT NULL, + `addedDate` INTEGER NOT NULL, + `keep` INTEGER NOT NULL, + PRIMARY KEY(`profileId`, `attendanceId`) + )""") + database.execSQL("""DROP INDEX IF EXISTS index_attendances_profileId""") + database.execSQL("""CREATE INDEX `index_attendances_profileId` ON `attendances` (`profileId`)""") + database.execSQL("""REPLACE INTO attendances ( + attendanceLessonTopic, attendanceLessonNumber, profileId, attendanceId, + attendanceBaseType, + attendanceTypeName, + attendanceTypeShort, + attendanceTypeSymbol, + attendanceTypeColor, attendanceDate, attendanceTime, attendanceSemester, teacherId, subjectId, addedDate, keep + ) SELECT + attendanceLessonTopic, NULL, profileId, attendanceId, + attendanceType, + CASE attendanceType WHEN 0 THEN "ob" WHEN 1 THEN "nb" WHEN 2 THEN "u" WHEN 3 THEN "zw" WHEN 4 THEN "sp" WHEN 5 THEN "su" WHEN 6 THEN "w" ELSE "?" END, + CASE attendanceType WHEN 0 THEN "ob" WHEN 1 THEN "nb" WHEN 2 THEN "u" WHEN 3 THEN "zw" WHEN 4 THEN "sp" WHEN 5 THEN "su" WHEN 6 THEN "w" ELSE "?" END, + CASE attendanceType WHEN 0 THEN "ob" WHEN 1 THEN "nb" WHEN 2 THEN "u" WHEN 3 THEN "zw" WHEN 4 THEN "sp" WHEN 5 THEN "su" WHEN 6 THEN "w" ELSE "?" END, + NULL, attendanceLessonDate, attendanceStartTime, attendanceSemester, teacherId, subjectId, 0, 1 + FROM _attendances""") + database.execSQL("""DROP TABLE _attendances""") + + // Events + database.execSQL("""ALTER TABLE events ADD COLUMN addedDate INTEGER NOT NULL DEFAULT 0""") + + // Grades + database.execSQL("""ALTER TABLE grades ADD COLUMN addedDate INTEGER NOT NULL DEFAULT 0""") + database.execSQL("""ALTER TABLE grades ADD COLUMN keep INTEGER NOT NULL DEFAULT 1""") + + // Lucky Numbers + database.execSQL("""ALTER TABLE luckyNumbers ADD COLUMN keep INTEGER NOT NULL DEFAULT 1""") + + // Messages + database.execSQL("""ALTER TABLE messages ADD COLUMN addedDate INTEGER NOT NULL DEFAULT 0""") + + // Notices + database.execSQL("""ALTER TABLE notices RENAME TO _notices""") + database.execSQL("""CREATE TABLE `notices` ( + `profileId` INTEGER NOT NULL, + `noticeId` INTEGER NOT NULL, + `noticeType` INTEGER NOT NULL, + `noticeSemester` INTEGER NOT NULL, + `noticeText` TEXT NOT NULL, + `noticeCategory` TEXT, + `noticePoints` REAL, + `teacherId` INTEGER NOT NULL, + `addedDate` INTEGER NOT NULL, + `keep` INTEGER NOT NULL, + PRIMARY KEY(`profileId`, `noticeId`) + )""") + database.execSQL("""DROP INDEX IF EXISTS index_notices_profileId""") + database.execSQL("""CREATE INDEX `index_notices_profileId` ON `notices` (`profileId`)""") + database.execSQL("""REPLACE INTO notices ( + profileId, noticeId, noticeType, noticeSemester, + noticeText, + noticeCategory, + noticePoints, + teacherId, addedDate, keep + ) SELECT + profileId, noticeId, noticeType, noticeSemester, + CASE noticeText WHEN NULL THEN "" ELSE noticeText END, + category, + CASE points WHEN 0 THEN NULL ELSE points END, + teacherId, 0, 1 + FROM _notices""") + database.execSQL("""DROP TABLE _notices""") + + // Teacher Absence + database.execSQL("""ALTER TABLE teacherAbsence ADD COLUMN addedDate INTEGER NOT NULL DEFAULT 0""") + database.execSQL("""ALTER TABLE teacherAbsence ADD COLUMN keep INTEGER NOT NULL DEFAULT 1""") + database.execSQL("""CREATE INDEX IF NOT EXISTS `index_teacherAbsence_profileId` ON `teacherAbsence` (`profileId`)""") + + // Timetable + database.execSQL("""ALTER TABLE timetable ADD COLUMN keep INTEGER NOT NULL DEFAULT 1""") + + // Metadata - copy AddedDate to entities + database.execSQL("""UPDATE grades SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = grades.profileId AND metadata.thingId = grades.gradeId AND metadata.thingType = 1), 0)""") + database.execSQL("""UPDATE notices SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = notices.profileId AND metadata.thingId = notices.noticeId AND metadata.thingType = 2), 0)""") + database.execSQL("""UPDATE attendances SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = attendances.profileId AND metadata.thingId = attendances.attendanceId AND metadata.thingType = 3), 0)""") + database.execSQL("""UPDATE events SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = events.profileId AND metadata.thingId = events.eventId AND (metadata.thingType = 4 OR metadata.thingType = 5)), 0)""") + database.execSQL("""UPDATE announcements SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = announcements.profileId AND metadata.thingId = announcements.announcementId AND metadata.thingType = 7), 0)""") + database.execSQL("""UPDATE messages SET addedDate = IFNULL((SELECT metadata.addedDate FROM metadata WHERE metadata.profileId = messages.profileId AND metadata.thingId = messages.messageId AND metadata.thingType = 8), 0)""") + + // Metadata - drop AddedDate column + database.execSQL("""ALTER TABLE metadata RENAME TO _metadata""") + database.execSQL("""CREATE TABLE metadata (profileId INTEGER NOT NULL, metadataId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, thingType INTEGER NOT NULL, thingId INTEGER NOT NULL, seen INTEGER NOT NULL, notified INTEGER NOT NULL)""") + database.execSQL("""DROP INDEX IF EXISTS index_metadata_profileId_thingType_thingId""") + database.execSQL("""CREATE UNIQUE INDEX index_metadata_profileId_thingType_thingId ON "metadata" (profileId, thingType, thingId)""") + database.execSQL("""INSERT INTO metadata (profileId, metadataId, thingType, thingId, seen, notified) SELECT profileId, metadataId, thingType, thingId, seen, notified FROM _metadata""") + database.execSQL("""DROP TABLE _metadata""") + + database.execSQL("""DELETE FROM endpointTimers WHERE endpointId IN (1080, 1081, 2050, 1090, 1010, 1014);""") + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration87.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration87.kt new file mode 100644 index 00000000..524cb559 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration87.kt @@ -0,0 +1,14 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-8. + */ + +package pl.szczodrzynski.edziennik.data.db.migration + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration87 : Migration(86, 87) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("ALTER TABLE attendances ADD COLUMN attendanceIsCounted INTEGER NOT NULL DEFAULT 1") + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration88.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration88.kt new file mode 100644 index 00000000..0dcd2f8c --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/migration/Migration88.kt @@ -0,0 +1,15 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-9. + */ + +package pl.szczodrzynski.edziennik.data.db.migration + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration88 : Migration(87, 88) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL("UPDATE endpointTimers SET endpointLastSync = 0 WHERE endpointId IN (1030, 1040, 1050, 1060, 1070, 1080);") + database.execSQL("UPDATE profiles SET empty = 1 WHERE loginStoreType = 4") + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt index c394c7cc..2ebb8c66 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/firebase/SzkolnyAppFirebase.kt @@ -114,7 +114,8 @@ class SzkolnyAppFirebase(val app: App, val profiles: List, val message: type = json.getLong("type") ?: 0, teacherId = json.getLong("teacherId") ?: -1, subjectId = json.getLong("subjectId") ?: -1, - teamId = team.id + teamId = team.id, + addedDate = json.getLong("addedDate") ?: System.currentTimeMillis() ) if (event.color == -1) event.color = null @@ -128,8 +129,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List, val message: if (event.type == Event.TYPE_HOMEWORK) Metadata.TYPE_HOMEWORK else Metadata.TYPE_EVENT, event.id, false, - true, - json.getLong("addedDate") ?: System.currentTimeMillis() + true ) val type = if (event.type == Event.TYPE_HOMEWORK) Notification.TYPE_NEW_SHARED_HOMEWORK else Notification.TYPE_NEW_SHARED_EVENT @@ -144,7 +144,7 @@ class SzkolnyAppFirebase(val app: App, val profiles: List, val message: profileId = profile.id, profileName = profile.name, viewId = if (event.type == Event.TYPE_HOMEWORK) MainActivity.DRAWER_ITEM_HOMEWORK else MainActivity.DRAWER_ITEM_AGENDA, - addedDate = metadata.addedDate + addedDate = event.addedDate ).addExtra("eventId", event.id).addExtra("eventDate", event.date.value.toLong()) notificationList += notification } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/bell/BellSyncTimeChooseDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/bell/BellSyncTimeChooseDialog.kt index 63601c76..4116ab83 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/bell/BellSyncTimeChooseDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/bell/BellSyncTimeChooseDialog.kt @@ -100,7 +100,7 @@ class BellSyncTimeChooseDialog( private fun loadTimeList() { launch { val timeItems = withContext(Dispatchers.Default) { - val lessons = app.db.timetableDao().getForDateNow(App.profileId, today) + val lessons = app.db.timetableDao().getAllForDateNow(App.profileId, today) val items = mutableListOf() lessons.forEach { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt index 03cc4879..c6a7b2f9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/day/DayDialog.kt @@ -86,7 +86,7 @@ class DayDialog( ) val lessons = withContext(Dispatchers.Default) { - app.db.timetableDao().getForDateNow(profileId, date) + app.db.timetableDao().getAllForDateNow(profileId, date) }.filter { it.type != Lesson.TYPE_NO_LESSONS } if (lessons.isNotEmpty()) { run { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt index d6d175d9..06b5f1a6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventManualDialog.kt @@ -484,7 +484,8 @@ class EventManualDialog( type = type ?: Event.TYPE_DEFAULT, teacherId = teacherId ?: -1, subjectId = subjectId ?: -1, - teamId = teamId ?: -1 + teamId = teamId ?: -1, + addedDate = editingEvent?.addedDate ?: System.currentTimeMillis() ).also { it.addedManually = true } @@ -497,8 +498,7 @@ class EventManualDialog( }, eventObject.id, true, - true, - editingEvent?.addedDate ?: System.currentTimeMillis() + true ) launch { @@ -536,10 +536,9 @@ class EventManualDialog( eventObject.apply { sharedBy = profile?.userCode sharedByName = profile?.studentNameLong + addedDate = System.currentTimeMillis() } - metadataObject.addedDate = System.currentTimeMillis() - api.runCatching(activity) { shareEvent(eventObject.withMetadata(metadataObject)) } ?: run { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt index 0e063758..20f3ad21 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/grade/GradeDetailsDialog.kt @@ -71,7 +71,7 @@ class GradeDetailsDialog( launch { val historyList = withContext(Dispatchers.Default) { - app.db.gradeDao().getAllWithParentIdNow(App.profileId, grade.id) + app.db.gradeDao().getByParentIdNow(App.profileId, grade.id) } if (historyList.isEmpty()) { b.historyVisible = false diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/AttendanceConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/AttendanceConfigDialog.kt new file mode 100644 index 00000000..e7d66ed5 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/AttendanceConfigDialog.kt @@ -0,0 +1,75 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-4. + */ + +package pl.szczodrzynski.edziennik.ui.dialogs.settings + +import android.annotation.SuppressLint +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.MainActivity +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.databinding.AttendanceConfigDialogBinding +import pl.szczodrzynski.edziennik.onChange + +class AttendanceConfigDialog( + val activity: AppCompatActivity, + private val reloadOnDismiss: Boolean = true, + val onShowListener: ((tag: String) -> Unit)? = null, + val onDismissListener: ((tag: String) -> Unit)? = null +) { + companion object { + const val TAG = "GradesConfigDialog" + } + + private val app by lazy { activity.application as App } + private val profileConfig by lazy { app.config.getFor(app.profileId).attendance } + + private lateinit var b: AttendanceConfigDialogBinding + private lateinit var dialog: AlertDialog + + init { run { + if (activity.isFinishing) + return@run + b = AttendanceConfigDialogBinding.inflate(activity.layoutInflater) + onShowListener?.invoke(TAG) + dialog = MaterialAlertDialogBuilder(activity) + .setTitle(R.string.menu_attendance_config) + .setView(b.root) + .setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() } + .setOnDismissListener { + saveConfig() + onDismissListener?.invoke(TAG) + if (reloadOnDismiss) (activity as? MainActivity)?.reloadTarget() + } + .create() + initView() + loadConfig() + dialog.show() + }} + + @SuppressLint("SetTextI18n") + private fun loadConfig() { + b.useSymbols.isChecked = profileConfig.useSymbols + b.groupConsecutiveDays.isChecked = profileConfig.groupConsecutiveDays + b.showPresenceInMonth.isChecked = profileConfig.showPresenceInMonth + } + + private fun saveConfig() { + // nothing to do here, yet + } + + private fun initView() { + b.useSymbols.onChange { _, isChecked -> + profileConfig.useSymbols = isChecked + } + b.groupConsecutiveDays.onChange { _, isChecked -> + profileConfig.groupConsecutiveDays = isChecked + } + b.showPresenceInMonth.onChange { _, isChecked -> + profileConfig.showPresenceInMonth = isChecked + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/GradesConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/GradesConfigDialog.kt index ad29e8f7..e74f3577 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/GradesConfigDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/GradesConfigDialog.kt @@ -4,6 +4,7 @@ package pl.szczodrzynski.edziennik.ui.dialogs.settings +import android.annotation.SuppressLint import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.view.isVisible @@ -59,6 +60,7 @@ class GradesConfigDialog( dialog.show() }} + @SuppressLint("SetTextI18n") private fun loadConfig() { b.customPlusCheckBox.isChecked = profileConfig.plusValue != null b.customPlusValue.isVisible = b.customPlusCheckBox.isChecked diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt index d8c11307..443d4734 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceAdapter.kt @@ -29,7 +29,7 @@ class TeacherAbsenceAdapter( override fun onBindViewHolder(holder: ViewHolder, position: Int) { val teacherAbsence: TeacherAbsenceFull = teacherAbsenceList[position] - holder.teacherAbsenceTeacher.text = teacherAbsence.teacherFullName + holder.teacherAbsenceTeacher.text = teacherAbsence.teacherName val time = when (teacherAbsence.timeFrom != null && teacherAbsence.timeTo != null) { true -> when (teacherAbsence.dateFrom.compareTo(teacherAbsence.dateTo)) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceDialog.kt index 836197f0..7ffab515 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/teacherabsence/TeacherAbsenceDialog.kt @@ -46,7 +46,7 @@ class TeacherAbsenceDialog( b.teacherAbsenceView.setHasFixedSize(true) b.teacherAbsenceView.layoutManager = LinearLayoutManager(activity) - app.db.teacherAbsenceDao().getAllByDateFull(profileId, date).observe(activity as LifecycleOwner, Observer { absenceList -> + app.db.teacherAbsenceDao().getAllByDate(profileId, date).observe(activity as LifecycleOwner, Observer { absenceList -> val adapter = TeacherAbsenceAdapter(activity, date, absenceList) b.teacherAbsenceView.adapter = adapter b.teacherAbsenceView.visibility = View.VISIBLE diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/agenda/AgendaFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/agenda/AgendaFragment.kt index 9ee93be9..ec212cea 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/agenda/AgendaFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/agenda/AgendaFragment.kt @@ -144,7 +144,7 @@ class AgendaFragment : Fragment(), CoroutineScope { if (!isAdded) return@launch - val lessons = withContext(Dispatchers.Default) { app.db.timetableDao().getAllChangesNow(app.profileId) } + val lessons = withContext(Dispatchers.Default) { app.db.timetableDao().getChangesNow(app.profileId) } val lessonChangeCounters = mutableListOf() lessons.forEach { lesson -> @@ -180,7 +180,7 @@ class AgendaFragment : Fragment(), CoroutineScope { val showTeacherAbsences = app.profile.getStudentData("showTeacherAbsences", true) if (showTeacherAbsences) { - val teacherAbsenceList = withContext(Dispatchers.Default) { app.db.teacherAbsenceDao().getAllFullNow(app.profileId) } + val teacherAbsenceList = withContext(Dispatchers.Default) { app.db.teacherAbsenceDao().getAllNow(app.profileId) } val teacherAbsenceCounters = mutableListOf() teacherAbsenceList.forEach { absence -> diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsAdapter.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsAdapter.java index 7f9b600d..ee88da07 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsAdapter.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/announcements/AnnouncementsAdapter.java @@ -59,17 +59,20 @@ public class AnnouncementsAdapter extends RecyclerView.Adapter { - if (announcement.text == null || (app.getProfile().getLoginStoreType() == LOGIN_TYPE_LIBRUS && !announcement.seen && app.getNetworkUtils().isOnline())) { + if (announcement.getText() == null || (app.getProfile().getLoginStoreType() == LOGIN_TYPE_LIBRUS && !announcement.getSeen() && app.getNetworkUtils().isOnline())) { EdziennikTask.Companion.announcementGet(App.Companion.getProfileId(), announcement).enqueue(requireContext()); } else { showAnnouncementDetailsDialog(announcement); @@ -162,14 +162,14 @@ public class AnnouncementsFragment extends Fragment { private void showAnnouncementDetailsDialog(AnnouncementFull announcement) { MaterialDialog dialog = new MaterialDialog.Builder(activity) - .title(announcement.subject) + .title(announcement.getSubject()) .customView(R.layout.dialog_announcement, true) .positiveText(R.string.ok) .show(); DialogAnnouncementBinding b = DialogAnnouncementBinding.bind(dialog.getCustomView()); - b.text.setText(announcement.teacherFullName+"\n\n"+ (announcement.startDate != null ? announcement.startDate.getFormattedString() : "-") + (announcement.endDate != null ? " do " + announcement.endDate.getFormattedString() : "")+"\n\n" +announcement.text); - if (!announcement.seen && app.getProfile().getLoginStoreType() != LOGIN_TYPE_LIBRUS) { - announcement.seen = true; + b.text.setText(announcement.getTeacherName() +"\n\n"+ (announcement.getStartDate() != null ? announcement.getStartDate().getFormattedString() : "-") + (announcement.getEndDate() != null ? " do " + announcement.getEndDate().getFormattedString() : "")+"\n\n" +announcement.getText()); + if (!announcement.getSeen() && app.getProfile().getLoginStoreType() != LOGIN_TYPE_LIBRUS) { + announcement.setSeen(true); AsyncTask.execute(() -> App.db.metadataDao().setSeen(App.Companion.getProfileId(), announcement, true)); if (recyclerView.getAdapter() != null) recyclerView.getAdapter().notifyDataSetChanged(); diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.java deleted file mode 100644 index 7b057395..00000000 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.java +++ /dev/null @@ -1,134 +0,0 @@ -package pl.szczodrzynski.edziennik.ui.modules.attendance; - -import android.content.Context; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.os.AsyncTask; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -import java.util.List; - -import pl.szczodrzynski.edziennik.App; -import pl.szczodrzynski.edziennik.R; -import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull; - -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_ABSENT; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_ABSENT_EXCUSED; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_BELATED; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_BELATED_EXCUSED; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_DAY_FREE; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_PRESENT; -import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_RELEASED; - -public class AttendanceAdapter extends RecyclerView.Adapter { - private Context context; - public List attendanceList; - - //getting the context and product list with constructor - public AttendanceAdapter(Context mCtx, List noticeList) { - this.context = mCtx; - this.attendanceList = noticeList; - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - //inflating and returning our view holder - LayoutInflater inflater = LayoutInflater.from(context); - View view = inflater.inflate(R.layout.row_attendance_item, parent, false); - return new ViewHolder(view); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - App app = (App) context.getApplicationContext(); - - AttendanceFull attendance = attendanceList.get(position); - - holder.attendanceLessonTopic.setText(attendance.lessonTopic); - holder.attendanceTeacher.setText(attendance.teacherFullName); - holder.attendanceSubject.setText(attendance.subjectLongName); - holder.attendanceDate.setText(attendance.lessonDate.getStringDmy()); - holder.attendanceTime.setText(attendance.startTime.getStringHM()); - - switch (attendance.type) { - case TYPE_DAY_FREE: - holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff166ee0, PorterDuff.Mode.MULTIPLY)); - holder.attendanceType.setText(R.string.attendance_free_day); - break; - case TYPE_ABSENT: - holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xfff44336, PorterDuff.Mode.MULTIPLY)); - holder.attendanceType.setText(R.string.attendance_absent); - break; - case TYPE_ABSENT_EXCUSED: - holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffaeea00, PorterDuff.Mode.MULTIPLY)); - holder.attendanceType.setText(R.string.attendance_absent_excused); - break; - case TYPE_BELATED: - holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffca28, PorterDuff.Mode.MULTIPLY)); - holder.attendanceType.setText(R.string.attendance_belated); - break; - case TYPE_BELATED_EXCUSED: - holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff4bb733, PorterDuff.Mode.MULTIPLY)); - holder.attendanceType.setText(R.string.attendance_belated_excused); - break; - case TYPE_RELEASED: - holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff9e9e9e, PorterDuff.Mode.MULTIPLY)); - holder.attendanceType.setText(R.string.attendance_released); - break; - case TYPE_PRESENT: - holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xffffae00, PorterDuff.Mode.MULTIPLY)); - holder.attendanceType.setText(R.string.attendance_present); - break; - default: - holder.attendanceType.getBackground().setColorFilter(new PorterDuffColorFilter(0xff03a9f4, PorterDuff.Mode.MULTIPLY)); - holder.attendanceType.setText("?"); - break; - } - - if (!attendance.seen) { - holder.attendanceLessonTopic.setBackground(context.getResources().getDrawable(R.drawable.bg_rounded_8dp)); - holder.attendanceLessonTopic.getBackground().setColorFilter(new PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY)); - attendance.seen = true; - AsyncTask.execute(() -> { - App.db.metadataDao().setSeen(App.Companion.getProfileId(), attendance, true); - //Intent i = new Intent("android.intent.action.MAIN").putExtra(MainActivity.ACTION_UPDATE_BADGES, "yes, sure"); - //context.sendBroadcast(i); - }); - } - else { - holder.attendanceLessonTopic.setBackground(null); - } - } - - @Override - public int getItemCount() { - return attendanceList.size(); - } - - class ViewHolder extends RecyclerView.ViewHolder { - - TextView attendanceType; - TextView attendanceLessonTopic; - TextView attendanceSubject; - TextView attendanceTeacher; - TextView attendanceDate; - TextView attendanceTime; - - ViewHolder(View itemView) { - super(itemView); - attendanceType = itemView.findViewById(R.id.attendanceType); - attendanceLessonTopic = itemView.findViewById(R.id.attendanceLessonTopic); - attendanceSubject = itemView.findViewById(R.id.attendanceSubject); - attendanceTeacher = itemView.findViewById(R.id.attendanceTeacher); - attendanceDate = itemView.findViewById(R.id.attendanceDate); - attendanceTime = itemView.findViewById(R.id.attendanceTime); - } - } -} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.kt new file mode 100644 index 00000000..0dcae97e --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceAdapter.kt @@ -0,0 +1,190 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-29. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance + +import android.animation.ObjectAnimator +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isInvisible +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull +import pl.szczodrzynski.edziennik.startCoroutineTimer +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.* +import pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder.* +import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import kotlin.coroutines.CoroutineContext + +class AttendanceAdapter( + val activity: AppCompatActivity, + val type: Int, + var onAttendanceClick: ((item: AttendanceFull) -> Unit)? = null +) : RecyclerView.Adapter(), CoroutineScope { + companion object { + private const val TAG = "AttendanceAdapter" + private const val ITEM_TYPE_ATTENDANCE = 0 + private const val ITEM_TYPE_DAY_RANGE = 1 + private const val ITEM_TYPE_MONTH = 2 + private const val ITEM_TYPE_SUBJECT = 3 + private const val ITEM_TYPE_TYPE = 4 + private const val ITEM_TYPE_EMPTY = 5 + const val STATE_CLOSED = 0 + const val STATE_OPENED = 1 + } + + private val app = activity.applicationContext as App + // optional: place the manager here + + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + var items = mutableListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return when (viewType) { + ITEM_TYPE_ATTENDANCE -> AttendanceViewHolder(inflater, parent) + ITEM_TYPE_DAY_RANGE -> DayRangeViewHolder(inflater, parent) + ITEM_TYPE_MONTH -> MonthViewHolder(inflater, parent) + ITEM_TYPE_SUBJECT -> SubjectViewHolder(inflater, parent) + ITEM_TYPE_TYPE -> TypeViewHolder(inflater, parent) + ITEM_TYPE_EMPTY -> EmptyViewHolder(inflater, parent) + else -> throw IllegalArgumentException("Incorrect viewType") + } + } + + override fun getItemViewType(position: Int): Int { + return when (items[position]) { + is AttendanceFull -> ITEM_TYPE_ATTENDANCE + is AttendanceDayRange -> ITEM_TYPE_DAY_RANGE + is AttendanceMonth -> ITEM_TYPE_MONTH + is AttendanceSubject -> ITEM_TYPE_SUBJECT + is AttendanceTypeGroup -> ITEM_TYPE_TYPE + is AttendanceEmpty -> ITEM_TYPE_EMPTY + else -> throw IllegalArgumentException("Incorrect viewType") + } + } + + private val onClickListener = View.OnClickListener { view -> + val model = view.getTag(R.string.tag_key_model) + if (model is AttendanceFull) { + onAttendanceClick?.invoke(model) + return@OnClickListener + } + if (model !is ExpandableItemModel<*>) + return@OnClickListener + expandModel(model, view) + } + + fun expandModel(model: ExpandableItemModel<*>?, view: View?, notifyAdapter: Boolean = true) { + model ?: return + val position = items.indexOf(model) + if (position == -1) + return + + view?.findViewById(R.id.dropdownIcon)?.let { dropdownIcon -> + ObjectAnimator.ofFloat( + dropdownIcon, + View.ROTATION, + if (model.state == STATE_CLOSED) 0f else 180f, + if (model.state == STATE_CLOSED) 180f else 0f + ).setDuration(200).start(); + } + + if (model is AttendanceDayRange || model is AttendanceMonth || model is AttendanceTypeGroup) { + // hide the preview, show summary + val preview = view?.findViewById(R.id.previewContainer) + val summary = view?.findViewById(R.id.summaryContainer) + val percentage = view?.findViewById(R.id.percentage) + preview?.isInvisible = model.state == STATE_CLOSED + summary?.isInvisible = model.state != STATE_CLOSED + percentage?.isVisible = model.state != STATE_CLOSED + } + + if (model.state == STATE_CLOSED) { + + val subItems = when { + model.items.isEmpty() -> listOf(AttendanceEmpty()) + else -> model.items + } + + model.state = STATE_OPENED + items.addAll(position + 1, subItems.filterNotNull()) + if (notifyAdapter) notifyItemRangeInserted(position + 1, subItems.size) + } + else { + val start = position + 1 + var end: Int = items.size + for (i in start until items.size) { + val model1 = items[i] + val level = (model1 as? ExpandableItemModel<*>)?.level ?: 3 + if (level <= model.level) { + end = i + break + } else { + if (model1 is ExpandableItemModel<*> && model1.state == STATE_OPENED) { + model1.state = STATE_CLOSED + } + } + } + + if (end != -1) { + items.subList(start, end).clear() + if (notifyAdapter) notifyItemRangeRemoved(start, end - start) + } + + model.state = STATE_CLOSED + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val item = items[position] + if (holder !is BindableViewHolder<*, *>) + return + + val viewType = when (holder) { + is AttendanceViewHolder -> ITEM_TYPE_ATTENDANCE + is DayRangeViewHolder -> ITEM_TYPE_DAY_RANGE + is MonthViewHolder -> ITEM_TYPE_MONTH + is SubjectViewHolder -> ITEM_TYPE_SUBJECT + is TypeViewHolder -> ITEM_TYPE_TYPE + is EmptyViewHolder -> ITEM_TYPE_EMPTY + else -> throw IllegalArgumentException("Incorrect viewType") + } + holder.itemView.setTag(R.string.tag_key_view_type, viewType) + holder.itemView.setTag(R.string.tag_key_position, position) + holder.itemView.setTag(R.string.tag_key_model, item) + + when { + holder is AttendanceViewHolder && item is AttendanceFull -> holder.onBind(activity, app, item, position, this) + holder is DayRangeViewHolder && item is AttendanceDayRange -> holder.onBind(activity, app, item, position, this) + holder is MonthViewHolder && item is AttendanceMonth -> holder.onBind(activity, app, item, position, this) + holder is SubjectViewHolder && item is AttendanceSubject -> holder.onBind(activity, app, item, position, this) + holder is TypeViewHolder && item is AttendanceTypeGroup -> holder.onBind(activity, app, item, position, this) + holder is EmptyViewHolder && item is AttendanceEmpty -> holder.onBind(activity, app, item, position, this) + } + + holder.itemView.setOnClickListener(onClickListener) + } + + fun notifyItemChanged(model: Any) { + startCoroutineTimer(1000L, 0L) { + val index = items.indexOf(model) + if (index != -1) + notifyItemChanged(index) + } + } + + override fun getItemCount() = items.size +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceBar.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceBar.kt new file mode 100644 index 00000000..70be9256 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceBar.kt @@ -0,0 +1,106 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-1. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.* +import android.text.TextPaint +import android.util.AttributeSet +import android.view.View +import pl.szczodrzynski.edziennik.dp +import pl.szczodrzynski.edziennik.utils.Colors +import kotlin.math.roundToInt + +/* https://github.com/JakubekWeg/Mobishit/blob/master/app/src/main/java/jakubweg/mobishit/view/AttendanceBarView.kt */ +class AttendanceBar : View { + + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) + + constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) + + private var attendancesList = listOf() + private val mainPaint = Paint(Paint.ANTI_ALIAS_FLAG) + private val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG).also { + it.textAlign = Paint.Align.CENTER + } + private var mPath = Path() + private var mCornerRadius: Float = 0.toFloat() + + init { + mCornerRadius = 4.dp.toFloat() + + if (isInEditMode) + setAttendanceData(listOf( + 0xff43a047.toInt() to 23, + 0xff009688.toInt() to 187, + 0xff3f51b5.toInt() to 46, + 0xff3f51b5.toInt() to 5, + 0xffffc107.toInt() to 5, + 0xff9e9e9e.toInt() to 26, + 0xff76ff03.toInt() to 34, + 0xffff3d00.toInt() to 8 + )) + } + + // color, count + private class AttendanceItem(var color: Int, var count: Int) + + fun setAttendanceData(list: List>) { + attendancesList = list.map { AttendanceItem(it.first, it.second) } + setWillNotDraw(false) + invalidate() + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + val r = RectF(0f, 0f, w.toFloat(), h.toFloat()) + mPath = Path().apply { + addRoundRect(r, mCornerRadius, mCornerRadius, Path.Direction.CW) + close() + } + } + + @SuppressLint("DrawAllocation", "CanvasSize") + override fun onDraw(canvas: Canvas?) { + canvas ?: return + + val sum = attendancesList.sumBy { it.count } + if (sum == 0) { + return + } + + canvas.clipPath(mPath) + + val top = paddingTop.toFloat() + val bottom = (height - paddingBottom).toFloat() + var left = paddingLeft.toFloat() + val unitWidth = (width - paddingRight - paddingLeft).toFloat() / sum.toFloat() + + textPaint.color = Color.BLACK + textPaint.textSize = 14.dp.toFloat() + + for (e in attendancesList) { + if (e.count == 0) + continue + + val width = unitWidth * e.count + mainPaint.color = e.color + canvas.drawRect(left, top, left + width, bottom, mainPaint) + + val percentage = (100f * e.count / sum).roundToInt().toString() + "%" + val textBounds = Rect() + textPaint.getTextBounds(percentage, 0, percentage.length, textBounds) + if (width > textBounds.width() + 8.dp && height > textBounds.height() + 2.dp) { + textPaint.color = Colors.legibleTextColor(e.color) + canvas.drawText(percentage, left + width / 2, bottom - height / 2 + textBounds.height()/2, textPaint) + } + + left += width + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceDetailsDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceDetailsDialog.kt new file mode 100644 index 00000000..ce49efbd --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceDetailsDialog.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-9. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance + +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.core.graphics.ColorUtils +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull +import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding +import pl.szczodrzynski.edziennik.setTintColor +import kotlin.coroutines.CoroutineContext + +class AttendanceDetailsDialog( + val activity: AppCompatActivity, + val attendance: AttendanceFull, + val onShowListener: ((tag: String) -> Unit)? = null, + val onDismissListener: ((tag: String) -> Unit)? = null +) : CoroutineScope { + companion object { + private const val TAG = "AttendanceDetailsDialog" + } + + private lateinit var app: App + private lateinit var b: AttendanceDetailsDialogBinding + private lateinit var dialog: AlertDialog + + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local variables go here + + init { run { + if (activity.isFinishing) + return@run + onShowListener?.invoke(TAG) + app = activity.applicationContext as App + b = AttendanceDetailsDialogBinding.inflate(activity.layoutInflater) + dialog = MaterialAlertDialogBuilder(activity) + .setView(b.root) + .setPositiveButton(R.string.close, null) + .setOnDismissListener { + onDismissListener?.invoke(TAG) + } + .show() + val manager = app.attendanceManager + + val attendanceColor = manager.getAttendanceColor(attendance) + b.attendance = attendance + b.devMode = App.debugMode + b.attendanceName.setTextColor(if (ColorUtils.calculateLuminance(attendanceColor) > 0.3) 0xaa000000.toInt() else 0xccffffff.toInt()) + b.attendanceName.background.setTintColor(attendanceColor) + + b.attendanceIsCounted.setText(if (attendance.isCounted) R.string.yes else R.string.no) + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.kt new file mode 100644 index 00000000..0af177f9 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.kt @@ -0,0 +1,117 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-30. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance + +import android.os.AsyncTask +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.data.db.entity.Metadata +import pl.szczodrzynski.edziennik.databinding.AttendanceFragmentBinding +import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog +import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.FragmentLazyPagerAdapter +import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem +import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetSeparatorItem +import kotlin.coroutines.CoroutineContext + +class AttendanceFragment : Fragment(), CoroutineScope { + companion object { + private const val TAG = "AttendanceFragment" + const val VIEW_SUMMARY = 0 + const val VIEW_DAYS = 1 + const val VIEW_MONTHS = 2 + const val VIEW_TYPES = 3 + const val VIEW_LIST = 4 + var pageSelection = 1 + } + + private lateinit var app: App + private lateinit var activity: MainActivity + private lateinit var b: AttendanceFragmentBinding + + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local/private variables go here + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + activity = (getActivity() as MainActivity?) ?: return null + context ?: return null + app = activity.application as App + b = AttendanceFragmentBinding.inflate(inflater) + b.refreshLayout.setParent(activity.swipeRefreshLayout) + return b.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + if (!isAdded) return + + activity.bottomSheet.prependItems( + BottomSheetPrimaryItem(true) + .withTitle(R.string.menu_attendance_config) + .withIcon(CommunityMaterial.Icon2.cmd_settings_outline) + .withOnClickListener(View.OnClickListener { + activity.bottomSheet.close() + AttendanceConfigDialog(activity, true, null, null) + }), + BottomSheetSeparatorItem(true), + BottomSheetPrimaryItem(true) + .withTitle(R.string.menu_mark_as_read) + .withIcon(CommunityMaterial.Icon.cmd_eye_check_outline) + .withOnClickListener(View.OnClickListener { + activity.bottomSheet.close() + AsyncTask.execute { App.db.metadataDao().setAllSeen(App.profileId, Metadata.TYPE_ATTENDANCE, true) } + Toast.makeText(activity, R.string.main_menu_mark_as_read_success, Toast.LENGTH_SHORT).show() + }) + ) + activity.gainAttention() + + if (pageSelection == 1) + pageSelection = app.config.forProfile().attendance.attendancePageSelection + + val pagerAdapter = FragmentLazyPagerAdapter( + fragmentManager ?: return, + b.refreshLayout, + listOf( + AttendanceSummaryFragment() to getString(R.string.attendance_tab_summary), + + AttendanceListFragment().apply { + arguments = Bundle("viewType" to VIEW_DAYS) + } to getString(R.string.attendance_tab_days), + + AttendanceListFragment().apply { + arguments = Bundle("viewType" to VIEW_MONTHS) + } to getString(R.string.attendance_tab_months), + + AttendanceListFragment().apply { + arguments = Bundle("viewType" to VIEW_TYPES) + } to getString(R.string.attendance_tab_types), + + AttendanceListFragment().apply { + arguments = Bundle("viewType" to VIEW_LIST) + } to getString(R.string.attendance_tab_list) + ) + ) + b.viewPager.apply { + offscreenPageLimit = 1 + adapter = pagerAdapter + currentItem = pageSelection + addOnPageSelectedListener { + pageSelection = it + app.config.forProfile().attendance.attendancePageSelection = it + } + b.tabLayout.setupWithViewPager(this) + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment_.java similarity index 93% rename from app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.java rename to app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment_.java index 2b5317a6..c46b3e09 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceFragment_.java @@ -51,7 +51,7 @@ import static pl.szczodrzynski.edziennik.data.db.entity.Attendance.TYPE_RELEASED import static pl.szczodrzynski.edziennik.data.db.entity.LoginStore.LOGIN_TYPE_VULCAN; import static pl.szczodrzynski.edziennik.data.db.entity.Metadata.TYPE_ATTENDANCE; -public class AttendanceFragment extends Fragment { +public class AttendanceFragment_ extends Fragment { private App app = null; private MainActivity activity = null; @@ -217,21 +217,21 @@ public class AttendanceFragment extends Fragment { subjectTotalCount = new LongSparseArray<>(); subjectAbsentCount = new LongSparseArray<>(); for (AttendanceFull attendance: attendanceList) { - if (app.getProfile().getLoginStoreType() == LOGIN_TYPE_VULCAN && attendance.type == TYPE_RELEASED) + if (app.getProfile().getLoginStoreType() == LOGIN_TYPE_VULCAN && attendance.getBaseType() == TYPE_RELEASED) continue; - int[] subjectTotal = subjectTotalCount.get(attendance.subjectId, new int[3]); - int[] subjectAbsent = subjectAbsentCount.get(attendance.subjectId, new int[3]); + int[] subjectTotal = subjectTotalCount.get(attendance.getSubjectId(), new int[3]); + int[] subjectAbsent = subjectAbsentCount.get(attendance.getSubjectId(), new int[3]); subjectTotal[0]++; - subjectTotal[attendance.semester]++; + subjectTotal[attendance.getSemester()]++; - if (attendance.type == TYPE_ABSENT || attendance.type == TYPE_ABSENT_EXCUSED) { + if (attendance.getBaseType() == TYPE_ABSENT || attendance.getBaseType() == TYPE_ABSENT_EXCUSED) { subjectAbsent[0]++; - subjectAbsent[attendance.semester]++; + subjectAbsent[attendance.getSemester()]++; } - subjectTotalCount.put(attendance.subjectId, subjectTotal); - subjectAbsentCount.put(attendance.subjectId, subjectAbsent); + subjectTotalCount.put(attendance.getSubjectId(), subjectTotal); + subjectAbsentCount.put(attendance.getSubjectId(), subjectAbsent); } } @@ -247,13 +247,13 @@ public class AttendanceFragment extends Fragment { List filteredList = new ArrayList<>(); for (AttendanceFull attendance: attendanceList) { - if (displayMode != MODE_YEAR && attendance.semester != displayMode) + if (displayMode != MODE_YEAR && attendance.getSemester() != displayMode) continue; - if (subjectIdFilter != -1 && attendance.subjectId != subjectIdFilter) + if (subjectIdFilter != -1 && attendance.getSubjectId() != subjectIdFilter) continue; - if (attendance.type != TYPE_PRESENT) + if (attendance.getBaseType() != TYPE_PRESENT) filteredList.add(attendance); - switch (attendance.type) { + switch (attendance.getBaseType()) { case TYPE_PRESENT: presentCount++; break; @@ -278,11 +278,12 @@ public class AttendanceFragment extends Fragment { b.attendanceView.setVisibility(View.VISIBLE); b.attendanceNoData.setVisibility(View.GONE); if ((adapter = (AttendanceAdapter) b.attendanceView.getAdapter()) != null) { - adapter.attendanceList = filteredList; + //adapter.setItems(filteredList); adapter.notifyDataSetChanged(); } else { - adapter = new AttendanceAdapter(getContext(), filteredList); + //adapter = new AttendanceAdapter(activity, true, null); + //adapter.setItems(filteredList); b.attendanceView.setAdapter(adapter); } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceListFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceListFragment.kt new file mode 100644 index 00000000..8e68cdbb --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceListFragment.kt @@ -0,0 +1,230 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-30. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.coroutines.* +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.MainActivity +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull +import pl.szczodrzynski.edziennik.databinding.AttendanceListFragmentBinding +import pl.szczodrzynski.edziennik.isNotNullNorEmpty +import pl.szczodrzynski.edziennik.startCoroutineTimer +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceDayRange +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceMonth +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceTypeGroup +import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment +import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject +import pl.szczodrzynski.edziennik.utils.models.Date +import kotlin.coroutines.CoroutineContext + +class AttendanceListFragment : LazyFragment(), CoroutineScope { + companion object { + private const val TAG = "AttendanceListFragment" + } + + private lateinit var app: App + private lateinit var activity: MainActivity + private lateinit var b: AttendanceListFragmentBinding + + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local/private variables go here + private val manager by lazy { app.attendanceManager } + private var viewType = AttendanceFragment.VIEW_DAYS + private var expandSubjectId = 0L + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + activity = (getActivity() as MainActivity?) ?: return null + context ?: return null + app = activity.application as App + b = AttendanceListFragmentBinding.inflate(inflater) + return b.root + } + + override fun onPageCreated(): Boolean { startCoroutineTimer(100L) { + if (!isAdded) return@startCoroutineTimer + + viewType = arguments?.getInt("viewType") ?: AttendanceFragment.VIEW_DAYS + expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L + + val adapter = AttendanceAdapter(activity, viewType) + var firstRun = true + + app.db.attendanceDao().getAll(App.profileId).observe(this@AttendanceListFragment, Observer { items -> this@AttendanceListFragment.launch { + if (!isAdded) return@launch + + // load & configure the adapter + adapter.items = withContext(Dispatchers.Default) { processAttendance(items) } + if (adapter.items.isNotNullNorEmpty() && b.list.adapter == null) { + b.list.adapter = adapter + b.list.apply { + setHasFixedSize(true) + layoutManager = LinearLayoutManager(context) + addOnScrollListener(onScrollListener) + } + } + adapter.notifyDataSetChanged() + setSwipeToRefresh(adapter.items.isNullOrEmpty()) + + if (firstRun) { + expandSubject(adapter) + firstRun = false + } + + // show/hide relevant views + b.progressBar.isVisible = false + if (adapter.items.isNullOrEmpty()) { + b.list.isVisible = false + b.noData.isVisible = true + } else { + b.list.isVisible = true + b.noData.isVisible = false + } + }}) + + adapter.onAttendanceClick = { + AttendanceDetailsDialog(activity, it) + } + }; return true} + + private fun expandSubject(adapter: AttendanceAdapter) { + var expandSubjectModel: GradesSubject? = null + if (expandSubjectId != 0L) { + expandSubjectModel = adapter.items.firstOrNull { it is GradesSubject && it.subjectId == expandSubjectId } as? GradesSubject + adapter.expandModel( + model = expandSubjectModel, + view = null, + notifyAdapter = false + ) + } + + startCoroutineTimer(500L) { + if (expandSubjectModel != null) { + b.list.smoothScrollToPosition( + adapter.items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0) + ) + } + } + } + + @Suppress("SuspendFunctionOnCoroutineScope") + private fun processAttendance(attendance: List): MutableList { + if (attendance.isEmpty()) + return mutableListOf() + + val groupConsecutiveDays = app.config.forProfile().attendance.groupConsecutiveDays + val showPresenceInMonth = app.config.forProfile().attendance.showPresenceInMonth + + if (viewType == AttendanceFragment.VIEW_DAYS) { + val items = attendance + .filter { it.baseType != Attendance.TYPE_PRESENT } + .groupBy { it.date } + .map { AttendanceDayRange( + rangeStart = it.key, + rangeEnd = null, + items = it.value.toMutableList() + ) } + .toMutableList() + + if (groupConsecutiveDays) { + items.sortByDescending { it.rangeStart } + val iterator = items.listIterator() + + if (!iterator.hasNext()) + return items.toMutableList() + var element = iterator.next() + while (iterator.hasNext()) { + var nextElement = iterator.next() + while (Date.diffDays(element.rangeStart, nextElement.rangeStart) <= 1 && iterator.hasNext()) { + if (element.rangeEnd == null) + element.rangeEnd = element.rangeStart + + element.items.addAll(nextElement.items) + element.rangeStart = nextElement.rangeStart + iterator.remove() + nextElement = iterator.next() + } + element = nextElement + } + } + + return items.toMutableList() + } + else if (viewType == AttendanceFragment.VIEW_MONTHS) { + val items = attendance + .groupBy { it.date.year to it.date.month } + .map { AttendanceMonth( + year = it.key.first, + month = it.key.second, + items = it.value.toMutableList() + ) } + + items.forEach { month -> + month.typeCountMap = month.items + .groupBy { it.typeObject } + .map { it.key to it.value.size } + .sortedBy { it.first } + .toMap() + + val totalCount = month.typeCountMap.entries.sumBy { + if (!it.key.isCounted || it.key.baseType == Attendance.TYPE_UNKNOWN) + 0 + else it.value + } + val presenceCount = month.typeCountMap.entries.sumBy { + when (it.key.baseType) { + Attendance.TYPE_PRESENT, + Attendance.TYPE_PRESENT_CUSTOM, + Attendance.TYPE_BELATED, + Attendance.TYPE_BELATED_EXCUSED, + Attendance.TYPE_RELEASED -> if (it.key.isCounted) it.value else 0 + else -> 0 + } + } + + month.percentage = if (totalCount == 0) + 0f + else + presenceCount.toFloat() / totalCount.toFloat() * 100f + + if (!showPresenceInMonth) + month.items.removeAll { it.baseType == Attendance.TYPE_PRESENT } + } + + return items.toMutableList() + } + else if (viewType == AttendanceFragment.VIEW_TYPES) { + val items = attendance + .groupBy { it.typeObject } + .map { AttendanceTypeGroup( + type = it.key, + items = it.value.toMutableList() + ) } + .sortedBy { it.items.size } + + items.forEach { type -> + type.percentage = if (attendance.isEmpty()) + 0f + else + type.items.size.toFloat() / attendance.size.toFloat() * 100f + + type.semesterCount = type.items.count { it.semester == app.profile.currentSemester } + } + + return items.toMutableList() + } + return attendance.filter { it.baseType != Attendance.TYPE_PRESENT }.toMutableList() + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceSummaryFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceSummaryFragment.kt new file mode 100644 index 00000000..5f61020b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceSummaryFragment.kt @@ -0,0 +1,306 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-4. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance + +import android.graphics.Color +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.AccelerateDecelerateInterpolator +import android.view.animation.Animation +import android.view.animation.Transformation +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.graphics.ColorUtils +import androidx.core.view.isInvisible +import androidx.core.view.isVisible +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.LinearLayoutManager +import kotlinx.coroutines.* +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull +import pl.szczodrzynski.edziennik.databinding.AttendanceSummaryFragmentBinding +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment.Companion.VIEW_SUMMARY +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceSubject +import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment +import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject +import pl.szczodrzynski.edziennik.utils.models.Date +import java.text.DecimalFormat +import kotlin.coroutines.CoroutineContext + +class AttendanceSummaryFragment : LazyFragment(), CoroutineScope { + companion object { + private const val TAG = "AttendanceSummaryFragment" + private var periodSelection = 0 + } + + private lateinit var app: App + private lateinit var activity: MainActivity + private lateinit var b: AttendanceSummaryFragmentBinding + + private val job: Job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + // local/private variables go here + private val manager by lazy { app.attendanceManager } + private var expandSubjectId = 0L + private var attendance = listOf() + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + activity = (getActivity() as MainActivity?) ?: return null + context ?: return null + app = activity.application as App + b = AttendanceSummaryFragmentBinding.inflate(inflater) + return b.root + } + + override fun onPageCreated(): Boolean { startCoroutineTimer(100L) { + if (!isAdded) return@startCoroutineTimer + + expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L + + val adapter = AttendanceAdapter(activity, VIEW_SUMMARY) + var firstRun = true + + app.db.attendanceDao().getAll(App.profileId).observe(this@AttendanceSummaryFragment, Observer { items -> this@AttendanceSummaryFragment.launch { + if (!isAdded) return@launch + + // load & configure the adapter + attendance = items + adapter.items = withContext(Dispatchers.Default) { processAttendance() } + if (adapter.items.isNotNullNorEmpty() && b.list.adapter == null) { + b.list.adapter = adapter + b.list.apply { + setHasFixedSize(false) + layoutManager = LinearLayoutManager(context) + isNestedScrollingEnabled = false + } + } + adapter.notifyDataSetChanged() + setSwipeToRefresh(adapter.items.isNullOrEmpty()) + + if (firstRun) { + expandSubject(adapter) + firstRun = false + } + + // show/hide relevant views + b.progressBar.isVisible = false + if (adapter.items.isNullOrEmpty()) { + b.statsLayout.isVisible = false + b.list.isVisible = false + b.noData.isVisible = true + } else { + b.statsLayout.isVisible = true + b.list.isVisible = true + b.noData.isVisible = false + } + }}) + + adapter.onAttendanceClick = { + AttendanceDetailsDialog(activity, it) + } + + b.toggleGroup.check(when (periodSelection) { + 0 -> R.id.allYear + 1 -> R.id.semester1 + 2 -> R.id.semester2 + else -> R.id.allYear + }) + b.toggleGroup.addOnButtonCheckedListener { _, checkedId, isChecked -> + if (!isChecked) + return@addOnButtonCheckedListener + periodSelection = when (checkedId) { + R.id.allYear -> 0 + R.id.semester1 -> 1 + R.id.semester2 -> 2 + else -> 0 + } + this@AttendanceSummaryFragment.launch { + adapter.items = withContext(Dispatchers.Default) { processAttendance() } + if (adapter.items.isNullOrEmpty()) { + b.statsLayout.isVisible = false + b.list.isVisible = false + b.noData.isVisible = true + } else { + b.statsLayout.isVisible = true + b.list.isVisible = true + b.noData.isVisible = false + } + adapter.notifyDataSetChanged() + } + } + }; return true} + + private fun expandSubject(adapter: AttendanceAdapter) { + var expandSubjectModel: GradesSubject? = null + if (expandSubjectId != 0L) { + expandSubjectModel = adapter.items.firstOrNull { it is GradesSubject && it.subjectId == expandSubjectId } as? GradesSubject + adapter.expandModel( + model = expandSubjectModel, + view = null, + notifyAdapter = false + ) + } + + startCoroutineTimer(500L) { + if (expandSubjectModel != null) { + b.list.smoothScrollToPosition( + adapter.items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0) + ) + } + } + } + + @Suppress("SuspendFunctionOnCoroutineScope") + private fun processAttendance(): MutableList { + val attendance = when (periodSelection) { + 0 -> attendance + 1 -> attendance.filter { it.semester == 1 } + 2 -> attendance.filter { it.semester == 2 } + else -> attendance + } + + if (attendance.isEmpty()) + return mutableListOf() + + val items = attendance + .groupBy { it.subjectId } + .map { AttendanceSubject( + subjectId = it.key, + subjectName = it.value.firstOrNull()?.subjectLongName ?: "", + items = it.value.toMutableList() + ) } + .sortedBy { it.subjectName.toLowerCase() } + + var totalCountSum = 0 + var presenceCountSum = 0 + + items.forEach { subject -> + subject.typeCountMap = subject.items + .groupBy { it.typeObject } + .map { it.key to it.value.size } + .sortedBy { it.first } + .toMap() + + val totalCount = subject.typeCountMap.entries.sumBy { + if (!it.key.isCounted || it.key.baseType == Attendance.TYPE_UNKNOWN) + 0 + else it.value + } + val presenceCount = subject.typeCountMap.entries.sumBy { + when (it.key.baseType) { + Attendance.TYPE_PRESENT, + Attendance.TYPE_PRESENT_CUSTOM, + Attendance.TYPE_BELATED, + Attendance.TYPE_BELATED_EXCUSED, + Attendance.TYPE_RELEASED -> if (it.key.isCounted) it.value else 0 + else -> 0 + } + } + totalCountSum += totalCount + presenceCountSum += presenceCount + + subject.percentage = if (totalCount == 0) + 0f + else + presenceCount.toFloat() / totalCount.toFloat() * 100f + + if (!false /* showPresenceInSubject */) + subject.items.removeAll { it.baseType == Attendance.TYPE_PRESENT } + } + + val typeCountMap = attendance + .groupBy { it.typeObject } + .map { it.key to it.value.size } + .sortedBy { it.first } + .toMap() + + val percentage = if (totalCountSum == 0) + 0f + else + presenceCountSum.toFloat() / totalCountSum.toFloat() * 100f + + launch { + b.attendanceBar.setAttendanceData(typeCountMap.map { manager.getAttendanceColor(it.key) to it.value }) + b.attendanceBar.isInvisible = typeCountMap.isEmpty() + + b.previewContainer.removeAllViews() + //val sum = typeCountMap.entries.sumBy { it.value }.toFloat() + typeCountMap.forEach { (type, count) -> + val layout = LinearLayout(activity) + val attendanceObject = Attendance( + profileId = 0, + id = 0, + baseType = type.baseType, + typeName = "", + typeShort = type.typeShort, + typeSymbol = type.typeSymbol, + typeColor = type.typeColor, + date = Date(0, 0, 0), + startTime = null, + semester = 0, + teacherId = 0, + subjectId = 0, + addedDate = 0 + ) + layout.addView(AttendanceView(activity, attendanceObject, manager)) + layout.addView(TextView(activity).also { + //it.setText(R.string.attendance_percentage_format, count/sum*100f) + it.text = count.toString() + it.setPadding(0, 0, 5.dp, 0) + }) + layout.setPadding(0, 8.dp, 0, 8.dp) + b.previewContainer.addView(layout) + } + + if (percentage == 0f) { + b.percentage.isInvisible = true + b.percentageCircle.isInvisible = true + } + else { + b.percentage.isVisible = true + b.percentageCircle.isVisible = true + b.percentage.setText(R.string.attendance_period_summary_format, percentage) + + val df = DecimalFormat("0.##") + b.percentageCircle.setProgressTextAdapter { value -> + df.format(value) + "%" + } + b.percentageCircle.maxProgress = 100.0 + animatePercentageIndicator(percentage.toDouble()) + } + } + + return items.toMutableList() + } + + private fun animatePercentageIndicator(targetProgress: Double) { + val startingProgress = b.percentageCircle.progress + val progressChange = targetProgress - startingProgress + + val a: Animation = object : Animation() { + override fun applyTransformation(interpolatedTime: Float, t: Transformation) { + val progress = startingProgress + (progressChange * interpolatedTime) + //if (interpolatedTime == 1f) + // progress = startingProgress + progressChange + + val color = ColorUtils.blendARGB(Color.RED, Color.GREEN, progress.toFloat() / 100.0f) + b.percentageCircle.progressColor = color + b.percentageCircle.setProgress(progress, 100.0) + } + + override fun willChangeBounds(): Boolean { + return false + } + } + a.duration = 1300 + a.interpolator = AccelerateDecelerateInterpolator() + b.percentageCircle.startAnimation(a) + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceView.kt new file mode 100644 index 00000000..dfe20eda --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceView.kt @@ -0,0 +1,84 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-29. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance + +import android.annotation.SuppressLint +import android.content.Context +import android.text.TextUtils +import android.util.AttributeSet +import android.util.TypedValue +import android.view.Gravity +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.appcompat.widget.AppCompatTextView +import androidx.core.graphics.ColorUtils +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.dp +import pl.szczodrzynski.edziennik.setTintColor +import pl.szczodrzynski.edziennik.utils.managers.AttendanceManager + +class AttendanceView : AppCompatTextView { + + @JvmOverloads + constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(context, attrs, defStyleAttr) + + constructor(context: Context, attendance: Attendance, manager: AttendanceManager) : this(context, null) { + setAttendance(attendance, manager, false) + } + + @SuppressLint("RestrictedApi") + fun setAttendance(attendance: Attendance?, manager: AttendanceManager, bigView: Boolean = false) { + if (attendance == null) { + visibility = View.GONE + return + } + visibility = View.VISIBLE + + val attendanceName = if (manager.useSymbols) + attendance.typeSymbol + else + attendance.typeShort + + val attendanceColor = manager.getAttendanceColor(attendance) + + text = when { + attendanceName.isBlank() -> " " + else -> attendanceName + } + + setTextColor(if (ColorUtils.calculateLuminance(attendanceColor) > 0.3) + 0xaa000000.toInt() + else + 0xccffffff.toInt()) + + setBackgroundResource(if (bigView) R.drawable.bg_rounded_8dp else R.drawable.bg_rounded_4dp) + background.setTintColor(attendanceColor) + gravity = Gravity.CENTER + + if (bigView) { + setTextSize(TypedValue.COMPLEX_UNIT_SP, 22f) + setAutoSizeTextTypeUniformWithConfiguration( + 14, + 32, + 1, + TypedValue.COMPLEX_UNIT_SP + ) + setPadding(2.dp, 2.dp, 2.dp, 2.dp) + } + else { + setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f) + setPadding(5.dp, 0, 5.dp, 0) + layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply { + setMargins(0, 0, 5.dp, 0) + } + maxLines = 1 + ellipsize = TextUtils.TruncateAt.END + measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) + } + } +} + diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceCount.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceCount.kt new file mode 100644 index 00000000..c2595885 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceCount.kt @@ -0,0 +1,20 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-30. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.models + +class AttendanceCount { + var normalSum = 0f + var normalCount = 0 + var normalWeightedSum = 0f + var normalWeightedCount = 0f + + var pointSum = 0f + + var pointAvgSum = 0f + var pointAvgMax = 0f + + var normalAvg: Float? = null + var pointAvgPercent: Float? = null +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceDayRange.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceDayRange.kt new file mode 100644 index 00000000..e993d785 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceDayRange.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-30. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.models + +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull +import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel +import pl.szczodrzynski.edziennik.utils.models.Date + +data class AttendanceDayRange( + var rangeStart: Date, + var rangeEnd: Date?, + override val items: MutableList = mutableListOf() +) : ExpandableItemModel(items) { + override var level = 1 + + var lastAddedDate = 0L + + var hasUnseen: Boolean = false + get() = field || items.any { it.baseType != Attendance.TYPE_PRESENT && !it.seen } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceEmpty.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceEmpty.kt new file mode 100644 index 00000000..46877f63 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceEmpty.kt @@ -0,0 +1,7 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-4. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.models + +class AttendanceEmpty diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceMonth.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceMonth.kt new file mode 100644 index 00000000..c6d14914 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceMonth.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-30. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.models + +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType +import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull +import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel + +data class AttendanceMonth( + val year: Int, + val month: Int, + override val items: MutableList = mutableListOf() +) : ExpandableItemModel(items) { + override var level = 1 + + var lastAddedDate = 0L + + var hasUnseen: Boolean = false + get() = field || items.any { it.baseType != Attendance.TYPE_PRESENT && !it.seen } + + var typeCountMap: Map = mapOf() + var percentage: Float = 0f +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceSubject.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceSubject.kt new file mode 100644 index 00000000..b56cd62a --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceSubject.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-4. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.models + +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType +import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull +import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel + +data class AttendanceSubject( + val subjectId: Long, + val subjectName: String, + override val items: MutableList = mutableListOf() +) : ExpandableItemModel(items) { + override var level = 1 + + var lastAddedDate = 0L + + var hasUnseen: Boolean = false + get() = field || items.any { it.baseType != Attendance.TYPE_PRESENT && !it.seen } + + var typeCountMap: Map = mapOf() + var percentage: Float = 0f +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceTypeGroup.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceTypeGroup.kt new file mode 100644 index 00000000..d6b431ec --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/models/AttendanceTypeGroup.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-8. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.models + +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType +import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull +import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel + +data class AttendanceTypeGroup( + val type: AttendanceType, + override val items: MutableList = mutableListOf() +) : ExpandableItemModel(items) { + override var level = 1 + + var lastAddedDate = 0L + + var hasUnseen: Boolean = false + get() = field || items.any { it.baseType != Attendance.TYPE_PRESENT && !it.seen } + + var percentage: Float = 0f + var semesterCount: Int = 0 +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/AttendanceViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/AttendanceViewHolder.kt new file mode 100644 index 00000000..3e7a1d32 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/AttendanceViewHolder.kt @@ -0,0 +1,79 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-30. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ContextThemeWrapper +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.concat +import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull +import pl.szczodrzynski.edziennik.databinding.AttendanceItemAttendanceBinding +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceDayRange +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceMonth +import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.utils.Themes +import pl.szczodrzynski.edziennik.utils.models.Week + +class AttendanceViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: AttendanceItemAttendanceBinding = AttendanceItemAttendanceBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "AttendanceViewHolder" + } + + override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceFull, position: Int, adapter: AttendanceAdapter) { + val manager = app.attendanceManager + val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme) + + val bullet = " • " + + b.attendanceView.setAttendance(item, manager, bigView = true) + + b.type.text = item.typeName + b.subjectName.text = item.subjectLongName ?: item.lessonTopic + b.dateTime.text = listOf( + Week.getFullDayName(item.date.weekDay), + item.date.formattedStringShort, + item.startTime?.stringHM, + item.lessonNumber?.let { app.getString(R.string.attendance_lesson_number_format, it) } + ).concat(bullet) + + if (item.showAsUnseen == null) + item.showAsUnseen = !item.seen + + b.unread.isVisible = item.showAsUnseen == true + if (!item.seen) { + manager.markAsSeen(item) + + val container = adapter.items.firstOrNull { + it is ExpandableItemModel<*> && it.items.contains(item) + } as? ExpandableItemModel<*> ?: return + + var hasUnseen = true + if (container is AttendanceDayRange) { + hasUnseen = container.items.any { !it.seen } + container.hasUnseen = hasUnseen + } + if (container is AttendanceMonth) { + hasUnseen = container.items.any { !it.seen } + container.hasUnseen = hasUnseen + } + + // check if the unseen status has changed + if (!hasUnseen) { + adapter.notifyItemChanged(container) + } + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/DayRangeViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/DayRangeViewHolder.kt new file mode 100644 index 00000000..b42d5e16 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/DayRangeViewHolder.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-30. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ContextThemeWrapper +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.concat +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.databinding.AttendanceItemDayRangeBinding +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter.Companion.STATE_CLOSED +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceView +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceDayRange +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.utils.Themes + +class DayRangeViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: AttendanceItemDayRangeBinding = AttendanceItemDayRangeBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "DayRangeViewHolder" + } + + override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceDayRange, position: Int, adapter: AttendanceAdapter) { + val manager = app.attendanceManager + val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme) + + b.title.text = listOf( + item.rangeStart.formattedString, + item.rangeEnd?.formattedString + ).concat(" - ") + + b.dropdownIcon.rotation = when (item.state) { + STATE_CLOSED -> 0f + else -> 180f + } + + b.unread.isVisible = item.hasUnseen + + b.previewContainer.visibility = if (item.state == STATE_CLOSED) View.VISIBLE else View.INVISIBLE + b.summaryContainer.visibility = if (item.state == STATE_CLOSED) View.INVISIBLE else View.VISIBLE + + b.previewContainer.removeAllViews() + + for (attendance in item.items) { + if (attendance.baseType == Attendance.TYPE_PRESENT_CUSTOM || attendance.baseType == Attendance.TYPE_UNKNOWN) + continue + b.previewContainer.addView(AttendanceView( + contextWrapper, + attendance, + manager + )) + } + if (item.items.isEmpty() || item.items.none { it.baseType != Attendance.TYPE_PRESENT_CUSTOM && it.baseType != Attendance.TYPE_UNKNOWN }) { + b.previewContainer.addView(TextView(contextWrapper).also { + it.setText(R.string.attendance_empty_text) + }) + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/EmptyViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/EmptyViewHolder.kt new file mode 100644 index 00000000..37fe635c --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/EmptyViewHolder.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-4. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.RecyclerView +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.databinding.AttendanceItemEmptyBinding +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceEmpty +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder + +class EmptyViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: AttendanceItemEmptyBinding = AttendanceItemEmptyBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "EmptyViewHolder" + } + + override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceEmpty, position: Int, adapter: AttendanceAdapter) { + + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/MonthViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/MonthViewHolder.kt new file mode 100644 index 00000000..71c139ac --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/MonthViewHolder.kt @@ -0,0 +1,99 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-30. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder + +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ContextThemeWrapper +import androidx.core.view.isInvisible +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.databinding.AttendanceItemMonthBinding +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter.Companion.STATE_CLOSED +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceView +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceMonth +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.utils.Themes +import pl.szczodrzynski.edziennik.utils.models.Date + +class MonthViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: AttendanceItemMonthBinding = AttendanceItemMonthBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "MonthViewHolder" + } + + override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceMonth, position: Int, adapter: AttendanceAdapter) { + val manager = app.attendanceManager + val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme) + + b.title.text = listOf( + app.resources.getStringArray(R.array.material_calendar_months_array).getOrNull(item.month - 1)?.fixName(), + item.year.toString() + ).concat(" ") + + b.dropdownIcon.rotation = when (item.state) { + STATE_CLOSED -> 0f + else -> 180f + } + + b.unread.isVisible = item.hasUnseen + + b.attendanceBar.setAttendanceData(item.typeCountMap.map { manager.getAttendanceColor(it.key) to it.value }) + + b.previewContainer.isInvisible = item.state != STATE_CLOSED + b.summaryContainer.isInvisible = item.state == STATE_CLOSED + b.percentage.isVisible = item.state == STATE_CLOSED + + b.previewContainer.removeAllViews() + + val sum = item.typeCountMap.entries.sumBy { it.value }.toFloat() + item.typeCountMap.forEach { (type, count) -> + val layout = LinearLayout(contextWrapper) + val attendance = Attendance( + profileId = 0, + id = 0, + baseType = type.baseType, + typeName = "", + typeShort = type.typeShort, + typeSymbol = type.typeSymbol, + typeColor = type.typeColor, + date = Date(0, 0, 0), + startTime = null, + semester = 0, + teacherId = 0, + subjectId = 0, + addedDate = 0 + ) + layout.addView(AttendanceView(contextWrapper, attendance, manager)) + layout.addView(TextView(contextWrapper).also { + //it.setText(R.string.attendance_percentage_format, count/sum*100f) + it.text = count.toString() + it.setPadding(0, 0, 5.dp, 0) + }) + layout.setPadding(0, 8.dp, 0, 0) + b.previewContainer.addView(layout) + } + + if (item.percentage == 0f) { + b.percentage.isVisible = false + b.percentage.text = null + b.summaryContainer.isVisible = false + b.summaryContainer.text = null + } + else { + b.percentage.setText(R.string.attendance_percentage_format, item.percentage) + b.summaryContainer.setText(R.string.attendance_period_summary_format, item.percentage) + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/SubjectViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/SubjectViewHolder.kt new file mode 100644 index 00000000..e439111d --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/SubjectViewHolder.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-4. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ContextThemeWrapper +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.databinding.AttendanceItemSubjectBinding +import pl.szczodrzynski.edziennik.setText +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter.Companion.STATE_CLOSED +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceSubject +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.utils.Themes + +class SubjectViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: AttendanceItemSubjectBinding = AttendanceItemSubjectBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "SubjectViewHolder" + } + + override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceSubject, position: Int, adapter: AttendanceAdapter) { + val manager = app.attendanceManager + val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme) + + b.title.text = item.subjectName + + b.dropdownIcon.rotation = when (item.state) { + STATE_CLOSED -> 0f + else -> 180f + } + + b.unread.isVisible = item.hasUnseen + + b.attendanceBar.setAttendanceData(item.typeCountMap.map { manager.getAttendanceColor(it.key) to it.value }) + + b.percentage.isVisible = true + + if (item.percentage == 0f) { + b.percentage.isVisible = false + b.percentage.text = null + } + else { + b.percentage.setText(R.string.attendance_percentage_format, item.percentage) + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/TypeViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/TypeViewHolder.kt new file mode 100644 index 00000000..a2f8b689 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/TypeViewHolder.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-5-8. + */ + +package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ContextThemeWrapper +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.concat +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.databinding.AttendanceItemTypeBinding +import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter +import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceTypeGroup +import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder +import pl.szczodrzynski.edziennik.utils.Themes +import pl.szczodrzynski.edziennik.utils.models.Date + +class TypeViewHolder( + inflater: LayoutInflater, + parent: ViewGroup, + val b: AttendanceItemTypeBinding = AttendanceItemTypeBinding.inflate(inflater, parent, false) +) : RecyclerView.ViewHolder(b.root), BindableViewHolder { + companion object { + private const val TAG = "TypeViewHolder" + } + + override fun onBind(activity: AppCompatActivity, app: App, item: AttendanceTypeGroup, position: Int, adapter: AttendanceAdapter) { + val manager = app.attendanceManager + val contextWrapper = ContextThemeWrapper(activity, Themes.appTheme) + + val type = item.type + b.title.text = type.typeName + + b.dropdownIcon.rotation = when (item.state) { + AttendanceAdapter.STATE_CLOSED -> 0f + else -> 180f + } + + b.unread.isVisible = item.hasUnseen + + b.details.text = listOf( + app.getString(R.string.attendance_percentage_format, item.percentage), + app.getString(R.string.attendance_type_yearly_format, item.items.size), + app.getString(R.string.attendance_type_semester_format, item.semesterCount) + ).concat(" • ") + + b.type.setAttendance(Attendance( + profileId = 0, + id = 0, + baseType = type.baseType, + typeName = "", + typeShort = type.typeShort, + typeSymbol = type.typeSymbol, + typeColor = type.typeColor, + date = Date(0, 0, 0), + startTime = null, + semester = 0, + teacherId = 0, + subjectId = 0, + addedDate = 0 + ), manager, bigView = false) + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/BehaviourFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/BehaviourFragment.java index 4ec8b4c6..d0b46f4a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/BehaviourFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/BehaviourFragment.java @@ -134,10 +134,10 @@ public class BehaviourFragment extends Fragment { List filteredList = new ArrayList<>(); for (NoticeFull notice: noticeList) { - if (displayMode != MODE_YEAR && notice.semester != displayMode) + if (displayMode != MODE_YEAR && notice.getSemester() != displayMode) continue; filteredList.add(notice); - switch (notice.type) { + switch (notice.getType()) { case Notice.TYPE_POSITIVE: praisesCount++; break; diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt index b1b85780..f495d61e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/behaviour/NoticesAdapter.kt @@ -41,10 +41,10 @@ class NoticesAdapter//getting the context and product list with constructor if (app.profile.loginStoreType == LOGIN_TYPE_MOBIDZIENNIK && false) { holder.noticesItemReason.text = bs(null, notice.category, "\n") + notice.text - holder.noticesItemTeacherName.text = app.getString(R.string.notices_points_format, notice.teacherFullName, if (notice.points > 0) "+" + notice.points else notice.points) + holder.noticesItemTeacherName.text = app.getString(R.string.notices_points_format, notice.teacherName, if (notice.points ?: 0f > 0) "+" + notice.points else notice.points) } else { holder.noticesItemReason.text = notice.text - holder.noticesItemTeacherName.text = notice.teacherFullName + holder.noticesItemTeacherName.text = notice.teacherName } holder.noticesItemAddedDate.text = Date.fromMillis(notice.addedDate).formattedString diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/editor/GradesEditorFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/editor/GradesEditorFragment.kt index 3e5978ee..6520ce54 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/editor/GradesEditorFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/editor/GradesEditorFragment.kt @@ -206,7 +206,7 @@ class GradesEditorFragment : Fragment() { gradeCountSemester = 0f averageSemester = 0f - app.db.gradeDao().getAllWhere(App.profileId, "subjectId = " + subject.id).observe(this, Observer { grades -> + app.db.gradeDao().getAllBySubject(App.profileId, subject.id).observe(this, Observer { grades -> for (grade in grades) { if (grade.type == Grade.TYPE_NORMAL) { if (grade.weight < 0) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/ExpandableItemModel.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/ExpandableItemModel.kt index c52ba310..20cb3ffc 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/ExpandableItemModel.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/ExpandableItemModel.kt @@ -6,7 +6,7 @@ package pl.szczodrzynski.edziennik.ui.modules.grades.models import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter.Companion.STATE_CLOSED -abstract class ExpandableItemModel(val items: MutableList) { +abstract class ExpandableItemModel(open val items: MutableList) { open var level: Int = 3 var state: Int = STATE_CLOSED } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt index e71b7819..2af79ba3 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt @@ -52,7 +52,7 @@ class GradeViewHolder( b.gradeWeight.text = weightText b.gradeWeight.isVisible = weightText != null - b.gradeTeacherName.text = grade.teacherFullName + b.gradeTeacherName.text = grade.teacherName b.gradeAddedDate.text = Date.fromMillis(grade.addedDate).let { it.getRelativeString(app, 5) ?: it.formattedStringShort } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/CounterActivity.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/CounterActivity.kt index 36002444..e337f720 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/CounterActivity.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/CounterActivity.kt @@ -51,7 +51,7 @@ class CounterActivity : AppCompatActivity(), CoroutineScope { withContext(Dispatchers.Default) { lessonList.apply { clear() - addAll(app.db.timetableDao().getForDateNow(App.profileId, Date.getToday()) + addAll(app.db.timetableDao().getAllForDateNow(App.profileId, Date.getToday()) .filter { it.type != Lesson.TYPE_NO_LESSONS && it.type != Lesson.TYPE_CANCELLED && it.type != Lesson.TYPE_SHIFTED_SOURCE diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeGradesCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeGradesCard.kt index 0e26eb2c..f1c3373e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeGradesCard.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeGradesCard.kt @@ -35,6 +35,7 @@ import pl.szczodrzynski.edziennik.ui.modules.home.HomeCard import pl.szczodrzynski.edziennik.ui.modules.home.HomeCardAdapter import pl.szczodrzynski.edziennik.ui.modules.home.HomeFragment import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.ItemGradesSubjectModel import kotlin.coroutines.CoroutineContext @@ -63,7 +64,7 @@ class HomeGradesCard( } holder.root += b.root - val sevenDaysAgo = System.currentTimeMillis() - 7 * 24 * 60 * 60 * 1000 + val sevenDaysAgo = Date.getToday().stepForward(0, 0, -7) app.db.gradeDao().getAllFromDate(profile.id, sevenDaysAgo).observe(fragment, Observer { grades.apply { @@ -120,7 +121,8 @@ class HomeGradesCard( val gradeName = GradeView( gradeItem.context, grade, - app.gradesManager + app.gradesManager, + periodGradesTextual = true ) totalWidth += gradeName.measuredWidth + 5.dp diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeLuckyNumberCard.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeLuckyNumberCard.kt index c5e4aede..31611e48 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeLuckyNumberCard.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/home/cards/HomeLuckyNumberCard.kt @@ -60,7 +60,7 @@ class HomeLuckyNumberCard( R.string.home_lucky_number_details b.subText.setText(subTextRes, profile.name ?: "", profile.studentNumber) - app.db.luckyNumberDao().getNearestFuture(profile.id, todayValue).observe(fragment, Observer { luckyNumber -> + app.db.luckyNumberDao().getNearestFuture(profile.id, today).observe(fragment, Observer { luckyNumber -> val isYours = luckyNumber?.number == profile.studentNumber val res: Pair> = when { luckyNumber == null -> R.string.home_lucky_number_no_info to emptyArray() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java index 3e3eac0e..2c84c878 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/settings/SettingsNewFragment.java @@ -52,6 +52,7 @@ import pl.szczodrzynski.edziennik.network.NetworkUtils; import pl.szczodrzynski.edziennik.sync.SyncWorker; import pl.szczodrzynski.edziennik.sync.UpdateWorker; import pl.szczodrzynski.edziennik.ui.dialogs.changelog.ChangelogDialog; +import pl.szczodrzynski.edziennik.ui.dialogs.settings.AttendanceConfigDialog; import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog; import pl.szczodrzynski.edziennik.ui.dialogs.settings.ProfileRemoveDialog; import pl.szczodrzynski.edziennik.ui.dialogs.sync.NotificationFilterDialog; @@ -885,6 +886,15 @@ public class SettingsNewFragment extends MaterialAboutFragment { .color(IconicsColor.colorInt(iconColor)) ).setOnClickAction(() -> new GradesConfigDialog(activity, false, null, null))); + items.add(new MaterialAboutActionItem( + getString(R.string.menu_attendance_config), + null, + new IconicsDrawable(activity) + .icon(CommunityMaterial.Icon.cmd_calendar_remove_outline) + .size(IconicsSize.dp(iconSizeDp)) + .color(IconicsColor.colorInt(iconColor)) + ).setOnClickAction(() -> new AttendanceConfigDialog(activity, false, null, null))); + registerCardAllowRegistrationItem = new MaterialAboutSwitchItem( getString(R.string.settings_register_allow_registration_text), getString(R.string.settings_register_allow_registration_subtext), @@ -1135,8 +1145,8 @@ public class SettingsNewFragment extends MaterialAboutFragment { new MaterialDialog.Builder(activity) .title(getString(R.string.settings_about_language_dialog_title)) .content(getString(R.string.settings_about_language_dialog_text)) - .items(getString(R.string.language_system), getString(R.string.language_polish), getString(R.string.language_english)) - .itemsCallbackSingleChoice(app.getConfig().getUi().getLanguage() == null ? 0 : app.getConfig().getUi().getLanguage().equals("pl") ? 1 : 2, (dialog, itemView, which, text) -> { + .items(getString(R.string.language_system), getString(R.string.language_polish), getString(R.string.language_english), getString(R.string.language_german)) + .itemsCallbackSingleChoice(app.getConfig().getUi().getLanguage() == null ? 0 : app.getConfig().getUi().getLanguage().equals("pl") ? 1 : app.getConfig().getUi().getLanguage().equals("en") ? 2 : 3, (dialog, itemView, which, text) -> { switch (which) { case 0: app.getConfig().getUi().setLanguage(null); @@ -1148,6 +1158,9 @@ public class SettingsNewFragment extends MaterialAboutFragment { case 2: app.getConfig().getUi().setLanguage("en"); break; + case 3: + app.getConfig().getUi().setLanguage("de"); + break; } activity.recreate(MainActivity.DRAWER_ITEM_SETTINGS); return true; diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/TimetableDayFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/TimetableDayFragment.kt index 848d0801..e6464d7d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/TimetableDayFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/timetable/TimetableDayFragment.kt @@ -105,7 +105,7 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope { override fun onPageCreated(): Boolean { // observe lesson database - app.db.timetableDao().getForDate(App.profileId, date).observe(this, Observer { lessons -> + app.db.timetableDao().getAllForDate(App.profileId, date).observe(this, Observer { lessons -> launch { val events = withContext(Dispatchers.Default) { app.db.eventDao().getAllByDateNow(App.profileId, date) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/TimeDropdown.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/TimeDropdown.kt index 08eda003..e60f24f8 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/TimeDropdown.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/TimeDropdown.kt @@ -87,7 +87,7 @@ class TimeDropdown : TextInputDropDown { ) } } else if (displayMode == DISPLAY_LESSONS && lessonsDate != null) { - val lessons = db.timetableDao().getForDateNow(profileId, lessonsDate!!) + val lessons = db.timetableDao().getAllForDateNow(profileId, lessonsDate!!) if (lessons.isEmpty()) { hours += Item( diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/widgets/luckynumber/WidgetLuckyNumberProvider.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/widgets/luckynumber/WidgetLuckyNumberProvider.kt index 014f5bbe..3dfa12f6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/widgets/luckynumber/WidgetLuckyNumberProvider.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/widgets/luckynumber/WidgetLuckyNumberProvider.kt @@ -48,7 +48,7 @@ class WidgetLuckyNumberProvider : AppWidgetProvider() { val tomorrowValue = tomorrow.value val profile = app.db.profileDao().getByIdNow(config.profileId) - val luckyNumber = app.db.luckyNumberDao().getNearestFutureNow(config.profileId, todayValue) + val luckyNumber = app.db.luckyNumberDao().getNearestFutureNow(config.profileId, today) val isYours = luckyNumber?.number == profile?.studentNumber var noNumberText = false diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/AttendanceManager.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/AttendanceManager.kt new file mode 100644 index 00000000..cd726797 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/AttendanceManager.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-28. + */ + +package pl.szczodrzynski.edziennik.utils.managers + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.data.db.entity.Attendance +import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType +import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull +import pl.szczodrzynski.edziennik.startCoroutineTimer +import kotlin.coroutines.CoroutineContext + +class AttendanceManager(val app: App) : CoroutineScope { + + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Default + + val useSymbols + get() = app.config.forProfile().attendance.useSymbols + + fun getTypeShort(baseType: Int): String { + return when (baseType) { + Attendance.TYPE_PRESENT -> "ob" + Attendance.TYPE_PRESENT_CUSTOM -> " " + Attendance.TYPE_ABSENT -> "nb" + Attendance.TYPE_ABSENT_EXCUSED -> "u" + Attendance.TYPE_RELEASED -> "zw" + Attendance.TYPE_BELATED -> "sp" + Attendance.TYPE_BELATED_EXCUSED -> "su" + Attendance.TYPE_DAY_FREE -> "w" + else -> "?" + } + } + + fun getAttendanceColor(baseType: Int): Int { + return when (baseType) { + Attendance.TYPE_PRESENT -> 0xff009688.toInt() + Attendance.TYPE_PRESENT_CUSTOM -> 0xff64b5f6.toInt() + Attendance.TYPE_ABSENT -> 0xffff3d00.toInt() + Attendance.TYPE_ABSENT_EXCUSED -> 0xff76ff03.toInt() + Attendance.TYPE_RELEASED -> 0xff9e9e9e.toInt() + Attendance.TYPE_BELATED -> 0xffffc107.toInt() + Attendance.TYPE_BELATED_EXCUSED -> 0xffffc107.toInt() + Attendance.TYPE_DAY_FREE -> 0xff43a047.toInt() + else -> 0xff64b5f6.toInt() + } + } + fun getAttendanceColor(typeObject: AttendanceType): Int { + return (if (useSymbols) typeObject.typeColor else null) + ?: if (typeObject.baseType == Attendance.TYPE_PRESENT_CUSTOM || !typeObject.isCounted) + typeObject.typeColor ?: 0xff64b5f6.toInt() + else getAttendanceColor(typeObject.baseType) + } + fun getAttendanceColor(attendance: Attendance): Int { + return (if (useSymbols) attendance.typeColor else null) + ?: if (attendance.baseType == Attendance.TYPE_PRESENT_CUSTOM || !attendance.isCounted) + attendance.typeColor ?: 0xff64b5f6.toInt() + else getAttendanceColor(attendance.baseType) + } + + /* _ _ _____ _____ _ __ _ + | | | |_ _| / ____| (_)/ _(_) + | | | | | | | (___ _ __ ___ ___ _| |_ _ ___ + | | | | | | \___ \| '_ \ / _ \/ __| | _| |/ __| + | |__| |_| |_ ____) | |_) | __/ (__| | | | | (__ + \____/|_____| |_____/| .__/ \___|\___|_|_| |_|\___| + | | + |*/ + fun markAsSeen(attendance: AttendanceFull) { + attendance.seen = true + startCoroutineTimer(500L, 0L) { + app.db.metadataDao().setSeen(attendance.profileId, attendance, true) + } + } +} diff --git a/app/src/main/res/drawable/face_1.jpg b/app/src/main/res/drawable/face_1.jpg new file mode 100644 index 00000000..77c899f9 Binary files /dev/null and b/app/src/main/res/drawable/face_1.jpg differ diff --git a/app/src/main/res/drawable/ic_badge_drawable.xml b/app/src/main/res/drawable/ic_badge_drawable.xml index 4ac8eb15..6091ab7b 100644 --- a/app/src/main/res/drawable/ic_badge_drawable.xml +++ b/app/src/main/res/drawable/ic_badge_drawable.xml @@ -1,10 +1,10 @@ - \ No newline at end of file + android:drawable="@mipmap/ic_launcher_foreground" /> + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index 1e056f8b..00000000 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_szkolny.xml b/app/src/main/res/layout/activity_szkolny.xml index 2a78de30..bdb2a6e7 100644 --- a/app/src/main/res/layout/activity_szkolny.xml +++ b/app/src/main/res/layout/activity_szkolny.xml @@ -32,6 +32,22 @@ + diff --git a/app/src/main/res/layout/attendance_config_dialog.xml b/app/src/main/res/layout/attendance_config_dialog.xml new file mode 100644 index 00000000..5d7db447 --- /dev/null +++ b/app/src/main/res/layout/attendance_config_dialog.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/attendance_details_dialog.xml b/app/src/main/res/layout/attendance_details_dialog.xml new file mode 100644 index 00000000..59ec37c3 --- /dev/null +++ b/app/src/main/res/layout/attendance_details_dialog.xml @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/attendance_fragment.xml b/app/src/main/res/layout/attendance_fragment.xml new file mode 100644 index 00000000..d83601f0 --- /dev/null +++ b/app/src/main/res/layout/attendance_fragment.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/attendance_item_attendance.xml b/app/src/main/res/layout/attendance_item_attendance.xml new file mode 100644 index 00000000..5362b4a6 --- /dev/null +++ b/app/src/main/res/layout/attendance_item_attendance.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/attendance_item_day_range.xml b/app/src/main/res/layout/attendance_item_day_range.xml new file mode 100644 index 00000000..4d33192f --- /dev/null +++ b/app/src/main/res/layout/attendance_item_day_range.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/attendance_item_empty.xml b/app/src/main/res/layout/attendance_item_empty.xml new file mode 100644 index 00000000..2d868178 --- /dev/null +++ b/app/src/main/res/layout/attendance_item_empty.xml @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/attendance_item_month.xml b/app/src/main/res/layout/attendance_item_month.xml new file mode 100644 index 00000000..6016a81c --- /dev/null +++ b/app/src/main/res/layout/attendance_item_month.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/attendance_item_subject.xml b/app/src/main/res/layout/attendance_item_subject.xml new file mode 100644 index 00000000..bcf95152 --- /dev/null +++ b/app/src/main/res/layout/attendance_item_subject.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/attendance_item_type.xml b/app/src/main/res/layout/attendance_item_type.xml new file mode 100644 index 00000000..2ec5cd4e --- /dev/null +++ b/app/src/main/res/layout/attendance_item_type.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/attendance_list_fragment.xml b/app/src/main/res/layout/attendance_list_fragment.xml new file mode 100644 index 00000000..36f54733 --- /dev/null +++ b/app/src/main/res/layout/attendance_list_fragment.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/attendance_summary_fragment.xml b/app/src/main/res/layout/attendance_summary_fragment.xml new file mode 100644 index 00000000..e4d96e80 --- /dev/null +++ b/app/src/main/res/layout/attendance_summary_fragment.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_grade_details.xml b/app/src/main/res/layout/dialog_grade_details.xml index 7a176c02..92781772 100644 --- a/app/src/main/res/layout/dialog_grade_details.xml +++ b/app/src/main/res/layout/dialog_grade_details.xml @@ -125,7 +125,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="0dp" - android:text="@{grade.teacherFullName}" + android:text="@{grade.teacherName}" android:textIsSelectable="true" tools:text="Janósz Kowalski" /> diff --git a/app/src/main/res/layout/grades_item_empty.xml b/app/src/main/res/layout/grades_item_empty.xml index 3ae82483..085eed62 100644 --- a/app/src/main/res/layout/grades_item_empty.xml +++ b/app/src/main/res/layout/grades_item_empty.xml @@ -16,6 +16,6 @@ android:textSize="18sp" android:textColor="?android:textColorSecondary" android:textStyle="italic" - android:text="Nie ma ocen w tym semestrze."/> + android:text="@string/grades_empty_text"/> diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 8678a9ed..036d09bc 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 8678a9ed..00000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index 19af4886..4d4e91f3 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..714e2112 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 2fa9f9b5..00000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_splash.png b/app/src/main/res/mipmap-hdpi/ic_splash.png index 01157da0..07e991c7 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_splash.png and b/app/src/main/res/mipmap-hdpi/ic_splash.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index 974e0572..4f7ceaf6 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..10fce979 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index e4f7d530..00000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_splash.png b/app/src/main/res/mipmap-mdpi/ic_splash.png index 76d8cfa7..f8ec3f40 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_splash.png and b/app/src/main/res/mipmap-mdpi/ic_splash.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index a8cd0957..1977b635 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..9e37ac8b Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 1a9a8476..00000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_splash.png b/app/src/main/res/mipmap-xhdpi/ic_splash.png index 05d3708c..ec734e0c 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_splash.png and b/app/src/main/res/mipmap-xhdpi/ic_splash.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index fc8be01c..c0de1cca 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..fa889dd2 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index e473eb24..00000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_splash.png b/app/src/main/res/mipmap-xxhdpi/ic_splash.png index 76cbfe9e..cd3f7c6d 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_splash.png and b/app/src/main/res/mipmap-xxhdpi/ic_splash.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 76d8cfa7..f8ec3f40 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 00000000..f8b05ce7 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 35bb174e..00000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_splash.png b/app/src/main/res/mipmap-xxxhdpi/ic_splash.png index fd054769..5bf6be7a 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_splash.png and b/app/src/main/res/mipmap-xxxhdpi/ic_splash.png differ diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml new file mode 100644 index 00000000..da0f36cd --- /dev/null +++ b/app/src/main/res/values-de/strings.xml @@ -0,0 +1,1233 @@ + + + + + Semester 1 + Semester 2 + + + Vom E-Klassenbuch + Nach Wert der Note + + + Vom Neuesten + Nach Schulfach-Name + + + Ereignis + Hausaufgabe + + + Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + + Januar + Februar + März + April + Mai + Juni + Juli + August + September + Oktober + November + Dezember + + + Abrechnen + (Hinzufügen) + Stundenplanänderungen + Abwesende Lehrer + %s vor + Möglicherweise läuft auf diesem Gerät ein App-Manager, der Probleme mit der automatischen Synchronisierung verursachen könnte.\n\nUm dieses Problem zu vermeiden, schalten Sie die Batterieoptimierung für Szkolny.eu aus.\n\nKlicken Sie OK, um zu den Systemeinstellungen zu gelangen, + Es gab ein Problem mit der Synchronisierung + Konnte Einstellungen nicht öffnen + Sind Sie sicher? + Zusammenfassung - Semester %d + Zusammenfassung - das Ganze Jahr + Abwesenheiten: + Davon unentschuldigt: + Verspätet: + Keine Abwesenheiten + Anwesenheiten: + Freigaben: + Alle Fächer + Zusammenfassung - Laden… + Zurück + Stellen Sie die Sync-Zeit der Glocke ein. Format: ±H:MM:SS + Falsches Format + Pause (%s) + Eine Kalibrierung ist unmöglich, weil es jetzt keine Lektionen gibt. Versuchen Sie es noch einmal, d.h. am Ende der Lektion oder in der Pause. Denken Sie daran, dass Sie dies sogar vor der geplanten Glockenzeit durchführen sollten + Wählen Sie die nächstgelegene Glocke, die synchronisiert werden soll + \n\nAktuelle Kalibrierung: %s + Klicken Sie auf das Glockensymbol, wenn die Glocke läutet. Die Zeit des Zählers wird mit die Glockenzeit kalibriert. \n\nGeplante Glockenzeit ist %s\n\nDie Differenz der Glockenzeit ist im Moment %s + Lektion %s (%s) + Möchten Sie die Kalibrierung zurücksetzen? + Die Glocke ist um %s ungenau + Zeit der zu synchronisierenden Glocke + Mit der Schulglocke kalibrieren + Kalender-App nicht gefunden + Abbrechen + Nach unten bewegen + Nach oben bewegen + Kommende Ereignisse + Gehe zu Noten + Noten - letzte 7 Tage + Keine Noten zu zeigen + Klicken Sie, um Ihre Nummer zu einstellen + Geben Sie Ihre Nummer aus dem Schulregister ein: + Ihre Nummer + Ihre Nummer ist %d. + %d ist heute die Glücknummer. + Gehe zum Stundenplan + Ereignisse: + In einem Moment: + Stundenplan für heute + Lektionsdauer: + %s %s
%s %s]]>
+ %s %s
%s %s]]>
+ Kein Lektionen an diesem Tag + Kein Stundenplan + Jetzt: + Stundenplan für %s + Der Lektion ist vorbei! Das sollten Sie niemals sehen! + Melden Sie einen Fehler! + Kommende Ereignisse + Neueste Noten + Glücksnummer + Stundenplan + Herunterladen + Sie haben Version %s, während %s verfügbar ist + Eine Aktualisierung ist verfügbar + Wählen Sie eine Datei aus + Wählen Sie ein Profil + Schließen + Komponieren + Komponieren + Konfigurieren + In die Zwischenablage kopiert + In die Zwischenablage kopieren + Synchronisieren + Wenn sich die App überhaupt nicht öffnet, können Sie versuchen, die Daten erneut herunterzuladen. Klicken Sie auf die Schaltfläche, dann starten Sie die App neu + Fehlerdetails + Nachricht vom Entwickler + It\'s not a bug. It\'s a feature! + Bitte melden Sie diesen Fehler + Den Fehler melden + Konnte keinen Bericht senden: + Fehlerbericht gesendet + Applikation neustarten + Synchronisieren + E-Klassenbuch:\\nEin unerwarteter Fehler ist aufgetreten.\\nEntschuldigung für das Problem + heute (%s) + morgen (%s) + Sie sollten sich nicht wirklich darum kümmern, was Sie hier sehen + Diese Einstellungen sind für die Entwickler dieser App gedacht, nicht für normale Benutzer.\n\n Sie sind in keiner Weise beschrieben, daher kann ihre Verwendung dazu führen, dass einige Funktionen in der App kaputt gehen, Ihr System beschädigt wird, Daten verloren gehen oder sogar ein Virus auf dem Akku installiert wird.\n\n Seien Sie vorsichtig und verwenden Sie sie mit Bedacht + Developer mode + Prognostizierter Durchschnitt für das %d Semester:\n%#.2f\n + Prognostizierter Jahresdurchschnitt:\n%#.2f\n + Abschlussnoten Durchschnitt für das %d Semester:\n%#.2f\n + Durchschnittliche Jahresabschlussnoten:\n%#.2f\n + Vorgeschlagene Durchschnittsnote für %d Semester:\n%#.2f\n + Vorgeschlagene Jahresdurchschnittsnote:\n%#.2f\n + Notendurchschnitte + Durchschnitts für %d Semester sind nicht verfügbar + Jahresdurchschnitt ist nicht verfügbar + %s - %s (%s Lektionen - %s Stunden %s Minuten) + Keine Ereignisse an diesem tag + Fehlerdetails + Hinzugefügt + Anhänge + Inhalt + Schulfach + Lehrer + Gruppe + Thema + Keine Ereignisse im ausgewählten Zeitraum + Den ganzen Tag + Sie müssen eine Gruppe (Klasse) von Empfängern auswählen, um das Ereignis freizugeben. + Farbe ändern + Wählen Sie eine Lektion aus + -- andere Zeit -- + Datum + Wählen Sie die Uhrzeit + Termin + nächste lektion %s + nächste %s (%s) + --andere Datum-- + heute (%s) + morgen (%s) + Lektion + Wählen Sie eine Lektion aus + Mehr Optionen + Um etwas teilen zu können, müssen Sie der Registrierung zustimmen.\nKeine Sorge, die Registrierung erfolgt automatisch, sodass Sie keine Daten angeben müssen.\n\nKlicken Sie auf Mehr, um mehr über die verarbeiteten Daten zu erfahren . + Sie sind nicht registriert + Kein Lektion an diesem Tag + Kein Schulfach + Kein Lehrer + Kein Gruppe + Es wird kein Zeitplan heruntergeladen… + Dieses Ereignis wurde von jemand anderem geteilt. Sie werden es nur auf Ihrem Gerät entfernen, aber Sie werden es nie wieder erhalten. + Dieses Ereignis wurde von Ihnen geteilt, alse wird es auch auf den Geräten Ihrer Klassenkameraden entfernt. + An Ihre Klasse senden + Die Veranstaltung wird an alle registrierten Personen aus der ausgewählten Gruppe in Ihrer Klasse gesendet. + Die Änderung dieses Ereignis wird an alle aus der ausgewählten Klasse gesendet + Dieses Ereignis wird von den Geräten Ihrer Klassenkameraden entfernt + Dies ist nicht dein Ereignis, daher wird eine Änderungsanfrage an %s geschick + Das können Sie nicht tun, weil Sie die Ereignisfreigabe in den Einstellungen deaktiviert haben. Wollen Sie sie aktivieren? + Ereignisfreigabe ist deaktiviert + Schulfach + Lehrer + Gruppe + Wählen Sie eine Gruppe aus + Lektion/Zeit + Wählen Sie die Zeit + Fügen Sie dem Zeitplan einen Eintrag hinzu + Thema + Geben Sie ein Thema ein + Typ + Wählen Sie einen Typ + Datum hinzugefügt + Kategorie + Klassendurchschnitt + Kommentar + Beschreibung + Notengeschichte + Note ID + Die Note wurde verbessert + (keine Kategorie) + (keine Beschreibung) + semester %d + Lehrer + Wert zum Durchschnitt + Gewicht %.2f + Note Farbe + Nach Wert der Note + Vom E-Klassenbuch + Vom Neuesten + Nach Schulfach-Name + Noten sortieren + Details + Klassenzimmer + Lektions-ID + Lektionsnummer + Lektion verschoben von %s + Lektion auf %s verschoben + Lehrer + Gruppe + Anzeigen + Keine Ereignisse in dieser Lektion + Sie können ein neues Ereignis manuell über die Schaltfläche Hinzufügen hinzufügen + Wählen Sie aus, welche Benachrichtigungen in Ihrem System und der Applikation angezeigt werden sollen + Ausgewählte Benachrichtigungen anzeigen + Profil erfolgreich entfernt + Möchten Sie das ausgewählte Ereignis entfernen? + Manuelles Ereignis + Manuelle Hausaufgaben + Manuelle Synchronisierung + Profil + Große Schrift + Opazität + Thema + nichts tun + getan + Nicht noch einmal fragen + Herunterladen %s (%s)? + Herunterladen… + Datei herunterladen + Die vorhandene Datei kann nicht entfernt werden.\nEs wird eine neue Datei erstellt. + Datei %s existiert bereits.\nWas wollen Sie damit machen? + Neu erstellen + Vorhanden öffnen + Überschreiben + Ein neues Profil hinzufügen + Anwendungsdaten exportieren + Hinzufügen oder Entfernen von Schülerprofilen + Es gibt keine Lektionen mehr für dieses Schulfach. Laden Sie den Stundenplan herunter und versuchen Sie es erneut + Eigenes Schulfach + Abbrechen + Schließen + Fehler melden… + Fehler melden + Synchronisierung abgebrochen + Erste Anmeldung + Benachrichtigungen erstellen + Profil %s synchronisieren… + Dank dieser Funktion kann Szkolny.eu Daten mit einem E-Klassenbuch synchronisieren. Sie können es schließen, weil es im Moment nichts tut. + Synchronisierungsdienst + Kontodaten herunterladen… + Schulankündigungen herunterladen… + Anwesenheit herunterladen… + Anwesenheitskategorien herunterladen… + Schülerverhaltens herunterladen… + Verhaltensnoten herunterladen… + Kalender herunterladen… + Klasseninformationen herunterladen… + Der Liste der Klassenzimmer herunterladen… + Daten herunterladen… + Beschreibende Schulnoten herunterladen… + Wörterbücher herunterladen + Ereigniskategorien herunterladen… + Kalenderereignissen herunterladen… + Prüfungen herunterladen + Kategorie der Schulnoten herunterladen… + Schulnoten Kommetare herunterladen… + Notes herunterladen… + Hausaufgaben herunterladen… + Lektionen herunterladen… + Glücksnummer herunterladen… + Nachrichten herunterladen… + Nachrichten im Posteingang herunterladen… + Nachrichten outbox herunterladen… + Kategorien von Schulnotizen herunterladen… + Notizen herunterladen… + Punktnoten herunterladen… + Vorschlagene Noten herunterladen… + Schulversammlungen mit den Eltern herunterladen… + Einrichten von Push-Benachrichtigungen… + Schulinfos herunterladen… + Studenteninformationen herunterladen… + Liste der Schulfächer herunterladen… + Lehrer-Abwesenheitstypen herunterladen… + Abwesenheiten von Lehrern herunterladen… + Lehrerliste herunterladen… + Klassengruppen herunterladen… + Stundenplan herunterladen… + Geräteinformationen herunterladen… + bei Edudziennik einloggen… + bei iDziennik einloggen… + bei iDziennik einloggen… + Einloggen in die API… + Einloggen bei Librus Nachrichten… + Einloggen bei Librus Portal… + Einloggen bei Librus Synergia… + Einloggen bei MobiDziennik… + Einloggen bei MobiDziennik… + Einloggen bei MobiDziennik API… + Einloggen bei Template API… + Einloggen bei Template WEB… + Einloggen bei Vulcan + Gemeinsame Ereignisse synchronisieren… + Erstellen von Benachrichtigungen… + Fehler + Fehlerdetails + Falsches Modul angegeben: %d Melden Sie es dem Anwendungsentwickler + Ihr iDziennik-Passwort ist abgelaufen. Sie müssen es in der Desktop-Version der Website ändern + Die Schulfach-ID wurde nicht angegeben + Ein Fehler ist aufgetreten! + Ein Fehler ist aufgetreten: %s + Konnte die Lektion nicht lesen + Klassenbuch-ID wurde nicht gefunden + Der Schüler ist in diesem Semester keinem Klassenbuch zugeordnet. + Schuljahr wurde nicht gefunden + Das Editieren der Ereignisse anderer Schuler ist noch nicht implementiert + Student-ID wurde nicht gefunden. Wahrscheinlich wurde die App aktualisiert.\n\nSynchronisieren Sie sie erneut oder versuchen Sie es in 5 Minuten erneut + Kod błędu: %d (in %s) + Hinzugefügt: + den ganzen Tag + Klassenereignis + %1$s von %2$s + %1$s von Ihnen + {cmd-share-variant} %1$s von %2$s + {cmd-share-variant} %1$s von Ihnen + Aufsatz + Prüfung + Schulausflug + Informationen + Hinzufügen %1$s von %2$s%3$s + Hinzufügen %1$s von Ihnen%3$s + Hinzugefügt %1$s%3$s + {cmd-share-variant} %1$s von %2$s%3$s + {cmd-share-variant} %1$s von Ihnen%3$s + Um das Ereignis freigeben zu können, müssen Sie die Serverregistrierungsoption aktivieren. Auf diese Weise können Sie Ereignisse erstellen und empfangen, die in Ihrer Klasse geteilt werden.\n\nWenn Sie auf OK klicken, wird diese automatisch aktiviert.\n\nStellen Sie sicher, dass Sie die Bedingungen lesen und akzeptieren. + Ereignisse teilen + Ereignis entfernen… + Ereignis speichern… + Ereignis teilen… + Ereignis aus dem Rest der Klasse entfernen… + Gemeinsames Ereignis entfernen… + Möchten Sie diese Aufgabe als erledigt markieren?\n\nEs wird nicht auf der Hauptseite und in den aktuellen Hausaufgaben angezeigt. Es wird weiterhin im Zeitplan verfügbar sein. + als erledigt markieren + andere + Projekt + Treffen mit den Eltern + Lesen + Gemeinsames Ereignis entfernen… + {cmd-share-variant} von %s + Ihnen + Ereignisse teilen + Ereignisse teilen… + kurzer Test + Hausaufgaben + Es gibt keine weiteren Ereignisse im Kalender. + Beenden + Häufig gestellte Fragen + Haben Sie eine Antwort auf Ihre Frage gefunden? Wenn nicht, können Sie mich direkt fragen + Haben Sie eine Antwort gefunden? + Wenn Sie eine Frage haben, können Sie zuerst die FAQ-Datenbank öffnen. Wollen Sie diese Website überprüfen? + Eine Frage stellen + Häufig gestellte Fragen - offen + FAQ-Datenbank öffnen + Geschichte des Gesprächs + Senden + Stimmt etwas nicht? Eine Frage, ein Vorschlag? + (keine Beschreibung) + %s (Finale) + %s Finale + %s (vorgeschlagene) + %s - vorgeschlagene + Ausgangspunkte + Ausgangspunkte: Semester %d + von %s + %s (jährlich) + %s - jährlich + %s (jährlich vorgeschlagen) + %s - jährlich vorgeschlagen + %s Punkte, %s Punkte + kein Durchschnitt + Durchschnitt: %s + Punkte: %s + %s Punkte + Summe: %s + %s Punkte + jährlich: %2$s + Zählen Sie den Durchschnitt, wenn alle Gewichte 0 sind + Erlaubt die Berechnung des Durchschnitts von Schulfächern, in denen alle Noten ein Gewicht von 0 haben (werden nicht zum Durchschnitt gezählt).\n\nWenn solche Schulfächer nicht zum Durchschnitt gezählt werden sollen, deaktivieren Sie das Kontrollkästchen neben dieser Einstellung. + Ausgewählte Noten vom Durchschnitt ausschließen + Trennen Sie die Noten mit einem Komma + Geben Sie Noten ein… + Korrigierte Noten aus der Liste ausblenden + Eigener "Minus" -Wert + Eigener "Plus" -Wert + Notenkonfiguration + Fügen Sie eine Note hinzu + Hinzufügen einer Note + Geben Sie das Gewicht der Note ein + Note ändern + Gewicht ändern + Schieben Sie den Grad nach links, um ihn zu ändern oder zu entfernen. Klicken Sie auf die Schaltfläche unten, um eine neue Note hinzuzufügen + Neue Note + Alle wiederherstellen + Semesterdurchschnitt nach Änderungen: + Ihr Semesterdurchschnitt: + Jahresdurchschnitt nach Änderungen: + Ihr Jahresdurchschnitt: + Gewicht %s + anderes Gewicht + (Verbesserung) %s + max %s Punkten + Keine Noten + Semester %d + Semester 1 + Semester 2 + Semester %d: %s + Semester %d: %s%% + Semester %d: %s Punkte + Semester %d + Semester %d + Die aktuellen Einstellungen könnten sich auf den Durchschnitt auswirken. Wenn Sie der Meinung sind, dass dies nicht der Fall ist, klicken Sie auf Konfigurieren. + Es gibt einen benutzerdefinierten \"minus\"/\"plus\"-Wert. Wenn Sie der Meinung sind, dass es nicht korrekt ist, klicken Sie auf Konfigurieren + Notendurchschnitte dienen nur zur Veranschaulichung und können je nach Schuleinstellungen variieren + *erwarteter Durchschnitt + *von den Abschulussnoten\nErwartete: %s + von Abschlussnoten + *von den vorgeschlagenen Noten\nErwartete: %s + *von den vorgeschlagenen Noten + Die vorhergesagte Note für ein bestimmtes Schulfach wird basierend auf dem aktuellen gewichteten Durchschnitt berechnet.\n\nDie Note ist eine Ganzzahl, die der Lehrer basierend auf dem Durchschnitt vergeben würde. Die Zahl wird aufgerundet, wenn der Dezimalteil ,75 überschreitet.\nBeispiel: 3,75 sowie 4,74 ergeben eine gute Note (4).\n\nDer erwartete Durchschnitt aller Schulfächer enthält die auf diese Weise berechneten Abschlussnoten. + Berechnung des Durchschnitts aller Fächer + Zu wenig Daten zur Berechnung des Durchschnitts + Durchschnitt der regulären Noten + Durchschnitt der Punktnoten + Erwartete Noten durchschnitt:\\n%s + Semester 1 + Semester 1 + Notenstatistiken + Jährlich + Wert: %s + Gewicht %s + nicht zum Durchschnitt gezählt + Ende des Jahres": %s + Ende des Jahres": %s%% + Ende des Jahres": %s Punkte + Ganzes Jahr: %s • %s + Hallo leeres Fragment + Hilfe + Die Weiterleitung von Benachrichtigungen ermöglicht es Ihnen, einen PC-Webbrowser zu koppeln, um Benachrichtigungen auf Ihrem Desktop zu empfangen. Dazu gehören neue Noten, Ereignisse, Hausaufgaben usw.\n\nKlicken Sie auf \"Benachrichtigungsweiterleitung\", um zu beginnen + Die Registrierung läuft automatisch beim ersten Login.\n\nEs werden einige Daten an den App-Server gesendet:\n- Ihre Schul-ID und Klassen-ID\n- Ihr Benutzername für die E-Klassenbuch\n- Ihr Vor- und Nachname\n\nDie einzigen Daten, die für andere Personen in Ihrer Klasse sichtbar sind, ist Ihr Name (beim Teilen von Ereignisse). Private Daten (wie Passwort, Noten usw.) werden nirgendwo versendet. Weitere Informationen finden Sie in den Datenschutz-Bestimmungen. + Erneut herunterladen + Ereignis bearbeiten + Gehe zum Fahrplan + Als erledigt markieren + Im Kalender speichern + Karten hinzufügen/entfernen + Wischen Sie nach links, um eine Karte zu entfernen, und halten Sie sie gedrückt, um sich zu bewegen. + %s • Ihre Nummer im Klassenbuch ist %d + %s • Klicken Sie, um Ihre Nummer Klassenbuch einzustellen, + Die Glücksnummer auf %s ist %d. + Keine Information über die Glücksnummer + Heute gibt es keine Glücksnummer. + %d ist die Glücksnummer von heute + %d ist die Glückszahl für morgen + Ihre Nummer wird Glück haben auf %s. + Heute ist Ihre Glücksnummer! + Morgen wird Ihre Glücksnummer sein! + Später: + keine Lektionen + Erste: %s + in einem Moment: %s + Jetzt: %s + %d Lektionen - %s bis %s + Verbleibende Lektionen: %d bis %s + Es gibt keinen Lektionen + In der kommenden Woche gibt es keinen Lektionen + Der Stundenplan wurde nicht heruntergeladen + Herunterladen + Der Stundenplan für die Woche %s ist noch nicht heruntergeladen + Es gibt keinen Stundenplan + Der Stundenplan wurde von Ihrer Schule noch nicht veröffentlicht. Wenden Sie sich für weitere Informationen an Ihren Klassenlehrer + Heute + Morgen (%1$s) + Bearbeiten + Es gibt keine Hausaufgaben. + Aktuell + Vergangenheit + Ich stimme zu + Ich stimme nicht zu + in %s + Falsches Format + Systemsprache verwenden + Neue Lektion + Pause + Lektion abgebrochen + Ersatz + Verschobene Lektion + Stundenplanänderung + Heute gibt es keine Lektionen mehr! + Laden… + Dieses Konto hat keine Studenten + Sie können nicht anmelden, weil diesem Konto kein Student zugeordnet ist.\n\nEinem Studenten auf der E-Klassenbuch-Seite einen Studenten zuordnen oder mit einem Konto anmelden, dem bereits ein Student zugeordnet ist + Registrierung erlauben + Einloggen + Möchten Sie die Anmeldung bei der App wirklich abbrechen? Angezeigte Profile werden nicht gespeichert. + Verwenden Sie die Kontodaten, mit denen Sie sich bei der Desktop-Version von Edudziennik einlogen + Einloggen - Edudziennik + Fehler: %s + Konto ist nicht aktiviert + Token ist abgelaufen. Erzeugen Sie ein neues Token + Falscher Servername + Falscher Code + Falscher Code oder PIN + Falsche E-Mail + Falsche login + Falsche login oder falsches Passwort + Falsche PIN + Falsche PIN. Verbleibende Versuche: %s + Falscher Schulname + Falsches Schulsymbol + Falsches Token + Falsches Token oder falsche PIN + Falscher Benutzername + Geben Sie Server-Adresse ein + keine Argumente angegeben + Geben Sie den Code ein + Geben Sie die E-Mail-Adresse ein + Geben Sie den Login ein + Geben Sie das Passwort ein + Geben Sie die PIN ein + Geben Sie den Namen der Schule ein + Geben Sie das Schulsymbol ein + Geben Sie den Token ein + Geben Sie den Benutzernamen ein + Altes Passwort verwendet + Anmeldung fehlgeschlagen + Falsche Login/falsches Passwort.\n\nWenn Sie glauben, dass dies ein Fehler ist, melden Sie ihn bitte. + Sie können jetzt die App verwenden. In den Einstellungen können Sie es an Ihre Anforderungen anpassen. + Ein weiteres Profil wurde hinzugefügt. Sie können zwischen Profilen wechseln, indem Sie in der App auf die Kopfzeile des Slideout-Menüs klicken. + Alles ist fertig! + Woher erhalten Sie diese Daten? + Name des Schulservers + Adresse + E-mail + Login + Login / e-mail + Passwort + PIN + Name der Schule + Symbol + Token + Benutzername + Verwenden Sie die Daten, mit denen Sie sich bei der Desktop-Version von iDziennik einloggen + Hilfe - iDziennik Progman + Melden Sie sich mit den Daten an, die Sie von Ihrer Schule erhalten haben. Bei Problemen verwenden Sie die Schaltfläche unter dem Formular. + Einloggen - iDziennik Progman + Librus - Einloggen + Um die App verwenden zu können, müssen Sie über ein Librus-Konto verfügen. Sie können sie unter portal.librus.pl erstellen. Verwenden Sie die Daten, die Sie an den im Bild markierten Stellen eingeben. + Hilfe - Librus + Melden Sie sich mit dem Token und der PIN an, die Sie in der Rubrik "mobile apps" von Librus Synergia erhalten können + Einloggen - Librus JST + Melden Sie sich mit Ihren Librus-Kontodaten an (zuvor unter Ihrer E-Mail-Adresse erstellt). Melden Sie sich nicht mit den von Ihrer Schule erhaltenen Daten an. Bei Problemen verwenden Sie die Schaltfläche unter dem Formular. + Einloggen - Librus + Ein Migrationsfehler ist aufgetreten. Er wurde bereits gemeldet, was bedeutet, dass ich versuchen werde, ihn zu beheben.\n\nSie können die App weiterhin verwenden. Bei Problemen versuchen Sie, das Profil zu entfernen und neu zu erstellen + Die App hat ein großes Update erhalten. Wenn bei der Operation Probleme auftreten, können Sie mich über die Hilfe und das Feedback im Menü kontaktieren. + Szkolny.eu wurde aktualisiert + Migration… + Adresse: + Login und Passwort: + Verwenden Sie die Daten, mit denen Sie sich in die Desktop-Version von Mobidziennik oder in die offizielle mobile App einloggen + Hilfe - MobiDziennik + Melden Sie sich mit den Daten an, die Sie von Ihrer Schule erhalten haben. Bei Problemen verwenden Sie die Schaltfläche unter dem Formular. + Einloggen - MobiDziennik + Anmelden… + Wählen Sie aus, welches E-Klassenbuch Ihre Schule verwendet. In späteren Phasen können Sie mehrere Konten aus verschiedenen E-Klassenbuchen hinzufügen. + Welches E-Klassenbuch benutzen Sie? + Fügen Sie einen Schüler hinzu + Sie müssen mindestens ein Profil auswählen, um es in der App zu speichern. + Kein Profil ausgewählt + Sie haben sich erfolgreich im E-Klassenbuch angemeldet. Wählen Sie die Schülerprofile aus, die Sie in der App speichern möchten.\n\nSie können über die Schaltfläche unten auf der Seite weitere Konten hinzufügen.\nWenn Sie möchten, können Sie später weitere Konten hinzufügen. + Es ist fast fertig + Sie können beispielsweise die Ereignisfreigabe und -benachrichtigung nicht mehr verwenden. + Möchten Sie die Registrierung wirklich deaktivieren? + Während der Synchronisation ist ein Fehler aufgetreten. Klicken Sie auf die Schaltfläche unten auf der Seite, um dies zu melden.\n\nDie Profile wurden gespeichert, sodass die Anwendung ordnungsgemäß funktionieren sollte . In diesem Fall können Sie die Option Hilfe und Feedback im Menü verwenden.\n. + Synchronisierungsfehler + Profil synchronisieren + Synchronisation… + Geräteregistrierung: + Melden Sie sich bei der Desktop-Version von Vulcan E-Klassenbuch an und wählen Sie die Option \"Dostęp mobilny\". Klicken Sie \"Zarejestruj urządzenie mobilne\". Sie können die Daten eingeben oder den QR-Code scannen und nur die PIN eingeben + Hilfe - Vulkan UONET+ + Erhaltene Daten: + QR-Code scannen + Wählen Sie \"Dostęp mobilny\" auf der E-Klassenbuch-Website, registrieren Sie ein neues Gerät und geben Sie die Daten ein. Bei Problemen verwenden Sie die Schaltfläche unter dem Formular. + Einloggen - Vulkan UONET+ + Hinzufügen… + Ansicht ändern + Generieren Sie einen Blockzeitplan + Notendurchschnitte + Noten Farbe + Alles als gelesen markieren + Erneut laden + Sortieren + F + M + Abbrechen + OK + S + S + D + Heute + D + M + Fügen Sie ein Ereignis hinzu + Eigene Ereigniss oder Hausaufgaben in der Zeitplan speichern + Fügen Sie einen neuen Schüler hinzu + Karten hinzufügen oder entfernen + Zeitplan + Zeitplan-Ansicht ändern + Schwarzes Brett + Anwesenheit + Fehlersuche + Hilfe und FAQ + Den Stundenplan als Bild speichern + Generiert Stundenplan für die ganze Woche + Noten + Berechnungsmethode für den Jahresend-Durchschnitt + Noten-Durchschnitte + Semester- und Jahresenddurchschnitte anzeigen + Noten Farbe + Einstellungen für Noten + Simulator für die Bearbeitung von Noten + Noten sortieren + Hilfe + Startseite + Hausaufgaben + Labor + Profile verwalten + Als gelesen markieren + Alles als gelesen markieren + Nachricht + Schreiben Sie eine Nachricht + Nachrichten + Verhalten + Benachrichtigungen + Löschen + Gelöschte Benachrichtigungen + Setzen Sie Ihre Klassenbuchnummer + Einstellungen + Synchronisieren + Alle synchronisieren + Schablone + Stundenplan + Stundenplan-Editor + Benachrichtigungen weiterleiten + Anhang kann nicht heruntergeladen werden + Beim Herunterladen des Anhangs ist ein interner Fehler aufgetreten. Möglicherweise handelt es sich um eine schwache Internetverbindung + Erneut herunterladen + Sind Sie sicher, dass Sie die Nachricht an ausgewählte Empfänger senden möchten? + Bestätigen Sie das Senden der Nachricht + Fügen Sie einen Anhang hinzu + Nachricht abbrechen + Entwurf speichern + Senden + Dieser Empfänger wurde bereits ausgewählt + Wählen Sie die Empfänger aus + Prüfen Sie, ob die ausgewählten Empfänger korrekt sind + Wählen Sie Empfänger aus der Kategorie %s aus, die Sie der Nachricht hinzufügen möchten + Bitte fügen Sie Empfänger hinzu + Senden + Geben Sie einen Betreff mit mindestens 3 Zeichen ein + Betreff + Geben Sie die Nachricht ein + Schreiben Sie eine Nachricht… + Schreiben Sie eine Nachricht + An + Sind Sie sicher, dass Sie die Nachricht löschen möchten? + Dadurch wird die Nachricht auf die Registerkarte \"Gelöscht\" in der App verschoben. Die Änderungen wirken sich nicht auf die E-Klassenbuch-Nachricht aus (sie wird dort nicht gelöscht). + Fehler beim Herunterladen von Nachrichten + Entwurf + Sie haben keine Nachrichten + Fehler beim herunterladen der Empfängerliste + &nbsp;%s, lies: %s, %s]]> + &nbsp;%s, lies: ja]]> + &nbsp;%s, lies: nein]]> + Antworten + %s um %s + Suche + %d Nachrichten gefunden + Nachricht wurde gesendet + Gelöscht + Posteingang + Gesendet + Mehr + Versuchen Sie, Wi-Fi oder mobile Daten einzuschalten + Sie sind offline + Weiter + Nein + Heute gibt es keine Lektionen! + Keine Berechtigungen + Nein, danke + Nicht jetzt + Keine Daten zum Verhalten der Schüler. + Andere: + %s, Punkte: %s + Loben: + Zusammenfassung - Laden… + Zusammenfassung - Semester %d + Zusammenfassung - das ganze Jahr + Warnungen: + Benachrichtigung + Abwesenheit + ntschuldigte Abwesenheit + Schulankündigung von %s: %s + %1$s auf Lektion %2$s auf Tag %3$s + %1$s am Tag %3$s + Verspätung + Entschuldigte Verspätung + Fehler beim Suchen nach Updates + Benachrichtigung über den Fortschritt der Datensynchronisation + Synchronisation + Benachrichtigungen über neue Daten im E-Klassenbuch + Benachrichtigungen + Benachrichtigungen über neue Daten im E-Klassenbuch (kein Ton - während ruhiger Zeiten) + Benachrichtigungen (Nachtruhe) + Benachrichtigungen über neue Versionen der App + App-Aktualisierungen + Benachrichtigungen über ein Problem, das eine Benutzerinteraktion erfordert (z.B. Captcha-Überprüfung). Es wird empfohlen, diese Kategorie eingeschaltet zu lassen + Aktion erforderlich + Klicken, um alle Benachrichtigungen zu sehen + Freier Tag + Updates herunterladen… + %1$s auf %2$s von %3$s + unbekanntes Subjekt + %1$s auf %2$s + Sind Sie sicher, dass Sie diese Einstellungen anwenden möchten?\n\nSie werden Informationen über einige Daten nicht sehen, so dass Sie wichtige Nachrichten oder Noten verpassen könnten.\n\nDie Einstellungen werden auf das aktuell ausgewählte Profil angewendet + Abbrechen + Szkolny.eu: Fehler + Fehler beim herunterladen von Daten für Profil %s + Erneut versuchen + Szkolny.eu: Herunterladen + Daten herunterladen + Aktualisieren + Neue Note (%s) auf %s + Hausaufgaben auf %s für %s + Hausaufgaben für %s + %s auf %s - %s + Heute ist %2$d die Glücksnummer. + Die Glücksnummer für %1$s ist %2$d. + Die Glücksnummer für morgen ist %2$d. + Du hast heute ein Glücksnummer (%2$d) + %1$s Sie werden die Glücksnummer haben! (%2$d) + Morgen ist es Ihre Glücksnummer! (%2$d) + Ungelesene Nachricht von %s: %s + Neue Benachrichtigungen: %d + Kein Update verfügbar + %s von %s auf %s + Neuer Notiz + Neues Lob + Neuer Warnung + Freigabe + %s geteilt %s auf %s - %s + %s geändert %s auf %s - %s + %s entfernt %s auf %s - %s + Neue Lehrerabwesenheit für %s + Anwesenheit + Profilarchivierung + Fehler + Nachricht vom Entwickler + Benachrichtigung + Glücksnummer + Schulankündigung + Neues Ereignis + Neue Note + Neue Hausaufgabe + Neue Nachricht + Neue geteilte Ereignis + Geteilte Hausaufgaben + Neue Abwesenheit von Lehrer + Neuer Notiz + Geteilte Ereignis entfernt + Server-Nachricht + Stundenplanänderung + Lektionsänderung + Update + Szkolny.eu: Update + Klicken Sie zum Herunterladen der Version %s + Update verfügbar + Librus: erfordert eine Lösung für die Captcha-Aufgabe. Klicken Sie hier, um sich weiter im Tagebuch anzumelden. + Das Problem, das die Synchronisierung verhindert, muss vom Benutzer gelöst werden. Klicken Sie für weitere Informationen + Erforderliche Aktion in der App + Keine Benachrichtigungen + OK + Öffnen + Öffnen %s + Keine App, die diesen Dateityp öffnet + Andere + Bitte warten + Datenschutzerklärung + Profil %s wurde archiviert. Die Synchronisierung wurde wegen der Möglichkeit eines Datenverlusts beim nächsten herunterladen deaktiviert.\n\nSie können die in der App in diesem Profil gespeicherten Daten weiterhin anzeigen.\n\nBachten Sie, dass einige Nachrichten möglicherweise nicht korrekt heruntergeladen werden + Profil wird archiviert + Profil %s wurde archiviert, weil das Schuljahr am %s endete. Synchronisirung ist deaktiviert, aber Sie können die Daten dieses Profils trotzdem durchsuchen. + Wenn dies zu lange dauert, versuchen Sie, die App neu zu starten + Wenn dies zu lange dauert, versuchen Sie, die App neu zu starten + App laden… + Einstellungen + Profil entfernen + Sind Sie sicher? + Sie versuchen, das Profil %s zu entfernen. Das bedeutet, dass Sie die Daten dieses Profils (Noten, Stundenpläne, Ereignissen…) aus der App entfernen.\n\nSie können die meisten Daten wiederherstellen, indem Sie sich erneut einloggen.\n\nWollen Sie wirklich Profil %s entfernen? + Einen QR-Code scannen + Niemals + Später + etzt bewerten + Zeigen Sie, dass Ihnen Szkolny.eu gefällt - bewerten Sie die App und machen Sie sie noch besser! + Aktualisieren + Die Registrierung erfolgt automatisch, wenn diese Option aktiviert ist. Sie können Ereignisse erstellen und empfangen, die mit anderen Schülern in Ihrer Klasse geteilt werden. Dank dessen können Sie Elemente, die nicht vom Lehrer gespeichert wurden, zum Klassenbuch hinzufügen.\n\nStellen Sie sicher, dass Sie die Bestimmungen der Datenschutzrichtlinie lesen und die Bestimmungen akzeptieren. + Server-Registrierung + Gemeinsame Ereignisse herunterladen… + Löschen + gelöschtet + Melden + Melden Sie einen Fehler + Zurücksetzen + Speichern + Gespeichert + Keine Schulankündigungen + Nachricht senden… + Änderungsprotokoll + Oder besser nicht klicken… + Klicken Sie hier, um eine unerwartete Ausnahme auszulösen + Treten Sie unserem Discord-Server bei! + Discord Server + Hinweis. Diese Option funktioniert möglicherweise auf einigen Geräten und in einigen Teilen der App nicht. + App-Sprache ändern + Deutsch + Sprache der App + Open-Source-Lizenzen + Datenschutzrichtlinie + E-Klassenbuch + © Kuba Szczodrzyński &amp;&amp; Kacper Ziubryniewicz\nSeptember 2018 - April 2020 + Klicken Sie hier, um nach Aktualisierungen zu suchen + Aktualisierung + Version + Mehr + Benachrichtigungsweiterleitung + Bild ändern + Name ändern + Geben Sie einen Namen für das Profil ein + Ändern Sie den Profilnamen + Profil + Deaktivieren Sie bestimmte Arten von Benachrichtigungen + Benachrichtigungen filtern + Benachrichtigungen über dieses Profil anzeigen + Benachrichtigung über neue Daten + Profilbild entfernen + Entfernt das Profil und alle seine Daten + Abmelden / Profil entfernen + Angemeldet als %s + Schließen Sie dieses Profil bei der Synchronisierung ein + Synchronisieren dieses Profil + Einige Funktionen wie Ereignisfreigabe oder Benachrichtigungsweiterleitung können nicht mehr verwendet werden.\n\nDie Funktion kann je nach ausgewähltem Profil aktiviert / deaktiviert werden. + Deaktivieren der Registrierung + Ein Fehler ist aufgetreten, aber Sie sollten sich nicht darum kümmern + Beendet: Erfolg + Benutzerregistrierung aufheben… + Deaktivieren der Registrierung + Durch die Verwendung der Funktion stimme ich den unten in den Einstellungen verfügbaren Datenschutzbestimmungen zu.\n\nDie Funktion kann je nach ausgewähltem Profil aktiviert / deaktiviert werden. + Registrierung erlauben + Nutzen Sie die zusätzlichen Funktionen der App + Registrierung erlauben + Durchschnittswerte beider Semester durchschnittlich + Durchschnitt beider Semester + Endnote von 1.sem + Durchschnitt von 2. sem + Note von 1.sem + Durchschnitt von 2. sem + Durchschnitt von 1. sem + Endnote von 2. sem + Durchschnitt von 1. sem + Note von 2. sem + Der Durchschnitt der Endnoten beider Semester + Durchschnitt der beiden Endnoten + Durchschnitt aller Noten aus beiden Semestern + Durchschnitt aller Noten + Wie berechnet man den Jahresenddurchschnitt?\n\nSie können für jedes Profil eine andere Option festlegen. + Berechnung des Jahresenddurchschnitts + Berechnung des Jahresenddurchschnitts + Aktuelle Zeit: (nicht eingestellt) + Aktuelle Zeit: %s + Passen Sie die Glockensynchronisation an + Beim Warten auf das Ende der Lektion + Zählen Sie die Zeit in Sekunden herunter + Zählen Sie die Note 0 nicht zum Durchschnitt + Dieses E-Register ist noch nicht implementiert + Sie erhalten keine geteilten Ereignisse mehr und können sie nicht mehr teilen. + Sie erhalten geteilte Ereignisse von anderen Personen in Ihrer Klasse + Teile Tests in deiner Klasse + Aktivieren Sie die Ereignisfreigabe + Zeigen Sie die Abwesenheiten von Lehrern im Zeitplan an + Schwarzes Brett + Anwesenheiten + Klasse freie Tage + Klassenzimmern + Ereignisse/Zeitplan + Noten + Hausaufgaben + Glücksnummer + Nachrichten - empfangen + Nachrichten - gesendet + Warnungen/Lob + Punktnoten + Eltern-Lehrer-Treffen + Schulfreie Tagen + Lektionsänderungen und Absagen + Abwesenheiten von Lehrern + Stundenplan + Alle 7 Tage heruntergeladen + Dies kann die Datensynchronisation beschleunigen + Aktivieren / Deaktivieren von Synchronisierungselementen + Passen Sie App-Benachrichtigungen an, z. B. Ton oder Vibration + Einstellungen für Systembenachrichtigungen + Nachtruhe + Startzeit einstellen + Endzeit einstellen + Deaktiviert + Keine Benachrichtigung ertönt von %s bis %s + eine Benachrichtigung ertönt von %s bis %s am nächsten Tag + Nachtruhe + Wann sollen Daten aus dem E-Register synchronisiert werden? + Automatische Synchronisierung + Deaktiviert + Daten alle %s herunterladen + Automatische Synchronisierung + Synchronisation und Benachrichtigungen + Über App-Aktualisierungen benachrichtigen + Benachrichtigungen auf Ihrem PC anzeigen + Benachrichtigungsweiterleitung + Beschränkt die Verwendung von Mobilfunkdaten + Nur über Wi-Fi synchronisieren + App-Hintergrund entfernen + App-Hintergrund festlegen + Bitte, tun Sie das nicht + App-Hintergrund ändern + Hintergrund der Menüüberschrift wiederherstellen + Hintergrund für Menüüberschrift festlegen + Hintergrund der Menüüberschrift ändern + Hinweis. Einige Funktionen sind möglicherweise in einigen E-Registern nicht verfügbar + Mini-Menü Tasten + Passen Sie die Mini-Menüschaltflächen an + Ein Menü auf der linken Seite anzeigen + Mini-Menü anzeigen + Öffnen Sie das Menü mit der Zurück-Taste + Schlittenfahrt im Schnee + Jingle Bells, Jingle Bells + Rosa + System + Thema + Aussehen + Teilen + Teilen über… + Daten Teilen… + Zeitplan + Noten + Hausaufgaben + Nachrichten + Stundenplan + Ein Fehler ist aufgetreten + Nach Datum + Nach Thema + Alle Fächer + Semester 1 + Semester 2 + Ganzes Jahr + Sicher! + Autorisierung + Benachrichtigungen erstellen + Kontoinformationen herunterladen + Kontoliste herunterladen + Zertifikat herunterladen + Tokens aktualisieren + Anmelden… + Datenkonvertierung + Push-Benachrichtigungen konfigurieren + Synchronisieren + Kontoinformationen herunterladen + Schulankündigungen herunterladen + Anwesenheit herunterladen + Anwesenheitskategorien herunterladen + Kategorie der Schulnoten aus Verhaltens herunterladen + Schulnoten aus Verhaltens herunterladen + Zeitplan herunterladen + Klasseninformationen herunterladen + Klassenzimmer herunterladen + Kategorie von Beschreibende Schulnoten herunterladen + Beschreibende Schulnoten herunterladen + Wörterbuch herunterladen + Ereigniskategorien herunterladen + Ereignisse herunterladen + Testen herunterladen + Notenkategorie herunterladen + Notenkommentare herunterladen + Notendetails herunterladen + Noten herunterladen + Hausaufgaben herunterladen + Glücksnummer herunterladen + Nachrichten herunterladen + Empfangene Nachrichten herunterladen + Gesendete Nachrichten herunterladen + Notizendetails herunterladen + Notizen herunterladen + Kategorien von Punktnoten herunterladen + Punktnoten herunterladen + Vorschlagene Noten herunterladen + Treffen mit Eltern herunterladen + Freien Schultagen herunterladen + Schulinformationen herunterladen + Geteilte Ereignisse herunterladen + Schulfächer herunterladen + Abwesenheit von Lehrern herunterladen + Abwesenheitsklassen für Lehrer herunterladen + Klassengruppen herunterladen + Stundenplan herunterladen + Stundenplan änderungen herunterladen + Lehrerliste herunterladen + Daten aktualisieren… + Laden… + synchronisiert + Nicht übereinstimmender Name des Benutzerkontos. Melden Sie einen Fehler. + Anwendungsserverfehler + Das ausgewählte Klassenbuch ist archiviert + Anhang kann nicht heruntergeladen werden. Das Netzwerklaufwerk mit dem Anhang ist nicht verfügbar. + Melden Sie einen Fehler + Synchronisierungsfehler + Ungültiges Gerät + Ungültige Anmeldedaten + Ungültiger Schulname oder verbotene Zeichen + Die Serveradresse ist ungültig. Stellen Sie sicher, dass es korrekt eingegeben wurde und keine Leerzeichen enthält. + Ungültiges Token angegeben. + Das LIBRUS-Konto hat die Verbindung zum Synergia-Konto verloren. Melden Sie sich bei portal.librus.pl oder der offiziellen Librus App an und befolgen Sie die Anweisungen zur Reparatur Ihres Kontos. + Das LIBRUS-Konto wurde nicht aktiviert. Aktivieren Sie das Konto mit der E-Mail, die Sie erhalten haben. + Anmeldefehler + Technische Pause\n\nVersuchen Sie es später erneut + API-Adresse nicht gefunden. Versuchen Sie erneut, sich am Gerät anzumelden. + Keine Internetverbindung\n\nMögliche E-Klassenbuch-Server sind überlastet oder haben eine technische Pause. + Login erfolgreich + Altes Passwort wurde verwendet + Dieses Profil wurde archiviert, daher ist eine Synchronisierung nicht möglich. + Profil nicht gefunden. Versuchen Sie, sich abzumelden und erneut anzumelden. + Diesem Konto sind keine Schüler zugeordnet.\n\nStellen Sie sicher, dass der Schüler Ihrem Konto zugewiesen ist. Überprüfen Sie die Desktop-Version der E-Klassenbuch-Website. + Student %s ist diesem Konto im Klassenbuch nicht zugeordnet (Login: %s).\n\nVersuchen Sie erneut, sich anzumelden, obwohl dies möglicherweise auf eine technische Unterbrechung im E-Klassenbuch hinweist. + Fehler beim Speichern der Daten. Melden Sie den Fehler. + Fehler beim Herstellen einer sicheren Verbindung. + Das Synergia-Konto wurde nicht von einem Elternteil oder Erziehungsberechtigten aktiviert.\n\nLoggen Sie sich in portal.librus.pl ein und befolgen Sie die Anweisungen, um es zu aktivieren. + Auszeit + Unbekannter Fehler + Stundenplan herunterladen + Alles + Ansagen herunterladen… + Anwesenheits herunterladen… + Noten herunterladen… + Hausaufgaben herunterladen… + Empfangene Nachrichten herunterladen… + Gesendete Nachrichten herunterladen + Notizen herunterladen… + Alles synchronisieren… + Wählen Sie aus, welche Elemente synchronisiert werden sollen. + Stundenplan synchronisieren… + Manuelle Synchronisierung + Login Fehler + Die neuesten Daten werden möglicherweise nicht angezeigt + Melden + API-Antwort einschließen (empfohlen) + Fehler melden + Synchronisierungsdienst + Klicken Sie auf das Symbol, um alle Daten als gelesen zu markieren. + Benachrichtigungen + Klicken Sie auf die Menüüberschrift, um das Profil eines Schülers zu ändern oder hinzuzufügen. Hier finden Sie auch eine manuelle Synchronisierung aller Profile. + Profile ändern + Klassenlehrer + Bibliothekar + Andere + Elternteil + Elternrat + Pädagoge / Psychologe + Schulleiter + Schulverwalter + Schulelternrat + Sekretariat + Spezialist + Schüler + Administrator / Superadministrator + Lehrer + Kategorie durchsuchen + Übermorgen + Vorgestern + Bernstein + Schwarz / OLED + Blau + Schokolade + Dunkel + Dunkelblau + Dunkelgrün + Dunkelviolett + Dunkelrot + Indigo + Hell + Hellblau + Hellgrün + Hellviolett + Hellrot + Hellgelb + Violett + rot + Stundenplan anzeigen + Kein Lektionen an diesem Tag: + Freier Tag + Mit Änderungen für diese Woche (%s bis %s) + Druckbar (keine Änderungen, weniger Farben) + Namen des Schülerprofils anzeigen + Mit Änderungen für die nächste Woche (%s bis %s) + Keine Stundenplanänderungen + Druckbar (weniger Farben) + Dies kann einige Sekunden dauern… + Stunden erstellen + Der Umfang des generierten Plans + Für ausgewählte Woche + Profilnamen anzeigen + Namen von Lehrern anzeigen + Der Stundenplan wurde im Verzeichnis Szkolny.eu als Bilddatei gespeichert. \ N \ nSie können ihn sofort öffnen oder freigeben. + Gemacht! + Lektion abgesagt + Ersatz + Ersatz: anstelle von %s + Die Lektion wurde auf einen anderen Tag verschoben + Die Lektion wurde von einem anderen Tag verschoben + Die Lektion wurde von %s %s verschoben + Die Lektion wurde von %s verschoben + "Die Lektion wurde auf %s %s verschoben" + Die Lektion wurde auf %s verschoben + Datum + Wählen Sie das Datum + Wählen Sie das Thema aus + Zeit + Wählen Sie die Zeit + %s (Lektion %d) + Für diesen Tag wurden keine eigenen Lektionen hinzugefügt. + Bei jeder %s Lektion + Beinhaltet einen Stundenplan für jede Woche + Nach Schulfach + Einmal + Wiederholen + (kein Klassenzimmer) + Keinen Lektionen an diesem Tag + Keinen Lektionen an diesem Tag + (kein Name) + Synchronisieren + Der Stundenplan für diese Woche wurde noch nicht synchronisiert + Kein Stundenplan + für Woche %s + Wenden Sie sich an Ihren Tutor, um den Stundenplan zu erhalten. + Der Zeitplan ist nicht öffentlich + Kein Zeitplan + Datum auswählen + Synchronisierung Stundenplan für die ausgewählte Woche… + Heute + Alles synchroniesieren + Debuggen + Feedback + Entwürfe + Empfangene Nachrichten + Gesendeten Nachrichten + Müll + Profilmanager + Wechseln Sie das Semester + Profil ändern… + Heute + Morgen + Synchronisieren… + Unbekannt + Update erforderlich + Sie müssen die App aktualisieren, um sie verwenden zu können. Klicken Sie zum Herunterladen auf OK. + Sie versuchen, eine ältere Version der App auszuführen, deren Daten möglicherweise beschädigt werden. + Gepaarte Browser + Keine Verbindung zum Server. + Gepaart %s + Keine gepaaren Browser. + paaren Sie den Browser + Möchten Sie diesen Browser wirklich löschen? + Das Token sieht nicht gültig aus + Das kannst du nicht machen + trennen + Sie müssen die automatische Registrierung aktivieren, um die Benachrichtigungsweiterleitung verwenden zu können.\n\nWählen Sie in den Anmeldeeinstellungen \"Registrierung zulassen\". + Was willst du machen? + Was ist das? + Was ist neu? + Passen Sie das Widget an + Kombiniert - alle Profile + Wird geladen… + Kein Profil + Glücksnummer + Keine Benachrichtigungen + Benachrichtigungen + Aktualisieren + Synchronisieren + Verwenden Sie eine größere Schriftart + Keinen Lektionen am ausgewählten Tag. + Keine Lektionen für den nächsten 7 Tage. + Der Stundenplan wurde nicht heruntergeladen.\n\nÖffnen Sie die App und synchronisieren Sie sie, um den Stundenplan herunterzuladen. + Das ausgewählte Profil ist nicht vorhanden.\nLöschen Sie das Widget und versuchen Sie es erneut. + Keine Lektionen für den nächsten 7 Tage. + Der Zeitplan wurde nicht heruntergeladen. + Stundenplan + Kombinierter Stundenplan + Ja + gestern + Sie sind offline. Versuchen Sie, Wi-Fi oder mobile Daten zu aktivieren. + Netzwerkverbindung + (Kind) + (Elternteil) +
diff --git a/app/src/main/res/values/errors.xml b/app/src/main/res/values/errors.xml index 8e9e043b..db856eb0 100644 --- a/app/src/main/res/values/errors.xml +++ b/app/src/main/res/values/errors.xml @@ -99,6 +99,7 @@ ERROR_LIBRUS_MESSAGES_NOT_FOUND ERROR_LOGIN_LIBRUS_API_INVALID_REQUEST ERROR_LIBRUS_MESSAGES_ATTACHMENT_NOT_FOUND + ERROR_LOGIN_LIBRUS_MESSAGES_TIMEOUT ERROR_LOGIN_MOBIDZIENNIK_WEB_INVALID_LOGIN ERROR_LOGIN_MOBIDZIENNIK_WEB_OLD_PASSWORD @@ -277,6 +278,7 @@ Nie znaleziono wiadomości. Mogła zostać usunięta. Nieprawidłowe dane dostępu. Sprawdź poprawność wprowadzonych danych. Nie znaleziono załącznika. Mógł zostać usunięty. + Logowanie Librus Wiadomości: ReCaptcha przekroczono czas oczekiwania. Nieprawidłowy login lub hasło Podano stare hasło diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 00000000..d75968c4 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,8 @@ + + + + + #0C92D6 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8812f92c..33c96675 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,15 +61,28 @@ u sp su + Grupuj kolejne dni na liście + Wyświetlaj obecność w widoku miesięcy + Konfiguracja frekwencji + Używaj symboli i kolorów wg dziennika + Widoczne po rozwinięciu listy + Nie ma tutaj żadnych nieobecności. w + lekcja %d + %.2f%% + Obecność w tym okresie: %.2f%% ob zw Podsumowanie - semestr %d  Podsumowanie - cały rok  + Dni + Lista + Miesiące + Podsumowanie Nieobecności: W tym nieusprawiedliwione: Spóźnienia: - Brak nieobecności. + Nie masz żadnych nieobecności. Obecności: Zwolnienia: Wszystkie przedmioty @@ -454,6 +467,7 @@ Twoja średnia na koniec roku: waga %s inna waga + Nie ma ocen w tym semestrze. (poprawa) %s max %s pkt Brak ocen w dzienniku. @@ -540,6 +554,7 @@ za %s Nieprawidłowy format English + Deutsch Polski Według systemu Nowa lekcja @@ -672,6 +687,7 @@ Zmień widok Terminarza Tablica ogłoszeń Frekwencja + Ustawienia frekwencji Debugowanie Pomoc i opinie Zapisz plan lekcji jako obraz @@ -907,6 +923,8 @@ Kliknij, aby sprawdzić aktualizacje Aktualizacja Wersja + Zaloguj konto ucznia/rodzica w aplikacji + Dodaj nowego ucznia Więcej Przekazywanie powiadomień Zmień obrazek @@ -1286,6 +1304,17 @@ wczoraj Jesteś offline. Spróbuj włączyć Wi-Fi lub dane komórkowe. Połączenie sieciowe + Wg typu + %d przez cały rok + %d w tym semestrze + Nauczyciel + Rodzaj + Data + Godzina + Temat lekcji + Liczona do puli? + ID frekwencji + ID rodzaju podstawowego Jaki masz e-dziennik w szkole? Wybierz z jakiego e-dziennika korzysta Twoja szkoła. Jeśli masz kilka kont w różnych dziennikach, będziesz mógł je dodać później. Librus/Synergia diff --git a/build.gradle b/build.gradle index 6143c28c..f4b6db5e 100644 --- a/build.gradle +++ b/build.gradle @@ -5,8 +5,8 @@ buildscript { kotlin_version = '1.3.61' release = [ - versionName: "4.0", - versionCode: 4000099 + versionName: "4.1", + versionCode: 4010099 ] setup = [ @@ -17,7 +17,7 @@ buildscript { ] versions = [ - gradleAndroid : "4.0.0-beta03", + gradleAndroid : '4.0.0-beta05', kotlin : ext.kotlin_version, ktx : "1.2.0",