From bb44fa066c5447a5559b2a03df1600b5b79afb8d Mon Sep 17 00:00:00 2001 From: Antoni Czaplicki <56671347+Antoni-Czaplicki@users.noreply.github.com> Date: Sat, 17 Sep 2022 12:31:35 +0200 Subject: [PATCH] [API/Vulcan] Migrate to new MessageBox API. (#134) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implement fetching vulcan messages from new api * Bump kotlin version and fix timetable card lessons size display * Revert formatting changes * Revert disabling message composing * Revert MultiDex changes and dependency upgrades * Add missing MultiDex dependency, update Google Services * Separate MessageBoxes sync, revert API data behavior changes * Revert migrating MessageRecipient to Kotlin * Use loginId from MessageBox address book * Revert using compatible HTML mode in Vulcan * Implement message meta and read status changing * Always set attachment lists * Fix setting tutor role description * Implement sending messages * Replace millis constant with WEEK * Revert timetable changes * Remove unused DataVulcan properties * Ensure UUID-format recipient IDs Co-authored-by: Kuba Szczodrzyński --- app/build.gradle | 1 + .../edziennik/data/api/Constants.kt | 10 ++- .../edziennik/data/api/Regexes.kt | 4 + .../data/api/edziennik/vulcan/DataVulcan.kt | 16 ++-- .../api/edziennik/vulcan/VulcanFeatures.kt | 10 ++- .../api/edziennik/vulcan/data/VulcanData.kt | 10 +++ .../api/edziennik/vulcan/data/VulcanHebe.kt | 36 +++++++- .../vulcan/data/hebe/HebeFilterType.kt | 1 + .../vulcan/data/hebe/VulcanHebeAddressbook.kt | 9 +- .../data/hebe/VulcanHebeAddressbook2.kt | 54 ++++++++++++ .../data/hebe/VulcanHebeMessageBoxes.kt | 43 ++++++++++ .../vulcan/data/hebe/VulcanHebeMessages.kt | 82 +++++++++++-------- .../hebe/VulcanHebeMessagesChangeStatus.kt | 18 ++-- .../vulcan/data/hebe/VulcanHebeSendMessage.kt | 82 ++++++++++++------- .../vulcan/data/hebe/VulcanHebeTeachers.kt | 1 + .../edziennik/data/api/models/Data.kt | 11 ++- .../compose/MessagesComposeFragment.kt | 2 +- .../edziennik/utils/html/BetterHtml.kt | 3 +- app/src/main/res/values-en/strings.xml | 2 +- build.gradle | 4 +- 20 files changed, 297 insertions(+), 102 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeAddressbook2.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessageBoxes.kt diff --git a/app/build.gradle b/app/build.gradle index 76b01ea8..2c525d23 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -142,6 +142,7 @@ dependencies { // Language cores implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation "androidx.multidex:multidex:2.0.1" coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5" // Android Jetpack diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Constants.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Constants.kt index 1cb7587a..888b183a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Constants.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Constants.kt @@ -92,7 +92,7 @@ val MOBIDZIENNIK_USER_AGENT = SYSTEM_USER_AGENT const val VULCAN_HEBE_USER_AGENT = "Dart/2.10 (dart:io)" const val VULCAN_HEBE_APP_NAME = "DzienniczekPlus 2.0" -const val VULCAN_HEBE_APP_VERSION = "21.02.09 (G)" +const val VULCAN_HEBE_APP_VERSION = "22.09.02 (G)" private const val VULCAN_API_DEVICE_NAME_PREFIX = "Szkolny.eu " private const val VULCAN_API_DEVICE_NAME_SUFFIX = " - nie usuwać" val VULCAN_API_DEVICE_NAME by lazy { @@ -116,9 +116,11 @@ const val VULCAN_HEBE_ENDPOINT_GRADE_SUMMARY = "api/mobile/grade/summary" const val VULCAN_HEBE_ENDPOINT_HOMEWORK = "api/mobile/homework" const val VULCAN_HEBE_ENDPOINT_NOTICES = "api/mobile/note" const val VULCAN_HEBE_ENDPOINT_ATTENDANCE = "api/mobile/lesson" -const val VULCAN_HEBE_ENDPOINT_MESSAGES = "api/mobile/message" -const val VULCAN_HEBE_ENDPOINT_MESSAGES_STATUS = "api/mobile/message/status" -const val VULCAN_HEBE_ENDPOINT_MESSAGES_SEND = "api/mobile/message" +const val VULCAN_HEBE_ENDPOINT_MESSAGEBOX = "api/mobile/messagebox" +const val VULCAN_HEBE_ENDPOINT_MESSAGEBOX_ADDRESSBOOK = "api/mobile/messagebox/addressbook" +const val VULCAN_HEBE_ENDPOINT_MESSAGEBOX_MESSAGES = "api/mobile/messagebox/message" +const val VULCAN_HEBE_ENDPOINT_MESSAGEBOX_STATUS = "api/mobile/messagebox/message/status" +const val VULCAN_HEBE_ENDPOINT_MESSAGEBOX_SEND = "api/mobile/messagebox/message" const val VULCAN_HEBE_ENDPOINT_LUCKY_NUMBER = "api/mobile/school/lucky" const val EDUDZIENNIK_USER_AGENT = "Szkolny.eu/${BuildConfig.VERSION_NAME}" diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Regexes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Regexes.kt index b42de2c4..e71a8d13 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Regexes.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/Regexes.kt @@ -20,6 +20,10 @@ object Regexes { """""".toRegex() } + val MESSAGE_META by lazy { + """^\[META:([A-z0-9-&=]+)]""".toRegex() + } + val MOBIDZIENNIK_GRADES_SUBJECT_NAME by lazy { 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 c6d797fa..0c2bc91c 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 @@ -222,15 +222,15 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app get() { mHebeContext = mHebeContext ?: profile?.getStudentData("hebeContext", null); return mHebeContext } set(value) { profile?.putStudentData("hebeContext", value) ?: return; mHebeContext = value } - private var mSenderAddressHash: String? = null - var senderAddressHash: String? - get() { mSenderAddressHash = mSenderAddressHash ?: profile?.getStudentData("senderAddressHash", null); return mSenderAddressHash } - set(value) { profile?.putStudentData("senderAddressHash", value) ?: return; mSenderAddressHash = value } + private var mMessageBoxKey: String? = null + var messageBoxKey: String? + get() { mMessageBoxKey = mMessageBoxKey ?: profile?.getStudentData("messageBoxKey", null); return mMessageBoxKey } + set(value) { profile?.putStudentData("messageBoxKey", value) ?: return; mMessageBoxKey = value } - private var mSenderAddressName: String? = null - var senderAddressName: String? - get() { mSenderAddressName = mSenderAddressName ?: profile?.getStudentData("senderAddressName", null); return mSenderAddressName } - set(value) { profile?.putStudentData("senderAddressName", value) ?: return; mSenderAddressName = value } + private var mMessageBoxName: String? = null + var messageBoxName: String? + get() { mMessageBoxName = mMessageBoxName ?: profile?.getStudentData("messageBoxName", null); return mMessageBoxName } + set(value) { profile?.putStudentData("messageBoxName", value) ?: return; mMessageBoxName = value } val apiUrl: String? get() { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/VulcanFeatures.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/VulcanFeatures.kt index 924d7ced..857a596e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/VulcanFeatures.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/VulcanFeatures.kt @@ -12,6 +12,7 @@ const val ENDPOINT_VULCAN_WEB_LUCKY_NUMBERS = 2010 const val ENDPOINT_VULCAN_HEBE_MAIN = 3000 const val ENDPOINT_VULCAN_HEBE_PUSH_CONFIG = 3005 const val ENDPOINT_VULCAN_HEBE_ADDRESSBOOK = 3010 +const val ENDPOINT_VULCAN_HEBE_ADDRESSBOOK_2 = 3011 const val ENDPOINT_VULCAN_HEBE_TIMETABLE = 3020 const val ENDPOINT_VULCAN_HEBE_EXAMS = 3030 const val ENDPOINT_VULCAN_HEBE_GRADES = 3040 @@ -19,10 +20,11 @@ const val ENDPOINT_VULCAN_HEBE_GRADE_SUMMARY = 3050 const val ENDPOINT_VULCAN_HEBE_HOMEWORK = 3060 const val ENDPOINT_VULCAN_HEBE_NOTICES = 3070 const val ENDPOINT_VULCAN_HEBE_ATTENDANCE = 3080 -const val ENDPOINT_VULCAN_HEBE_MESSAGES_INBOX = 3090 -const val ENDPOINT_VULCAN_HEBE_MESSAGES_SENT = 3100 const val ENDPOINT_VULCAN_HEBE_TEACHERS = 3110 const val ENDPOINT_VULCAN_HEBE_LUCKY_NUMBER = 3200 +const val ENDPOINT_VULCAN_HEBE_MESSAGE_BOXES = 3500 +const val ENDPOINT_VULCAN_HEBE_MESSAGES_INBOX = 3510 +const val ENDPOINT_VULCAN_HEBE_MESSAGES_SENT = 3520 val VulcanFeatures = listOf( // timetable @@ -85,6 +87,8 @@ val VulcanFeatures = listOf( Feature(LOGIN_TYPE_VULCAN, FEATURE_ALWAYS_NEEDED, listOf( ENDPOINT_VULCAN_HEBE_MAIN to LOGIN_METHOD_VULCAN_HEBE, ENDPOINT_VULCAN_HEBE_ADDRESSBOOK to LOGIN_METHOD_VULCAN_HEBE, - ENDPOINT_VULCAN_HEBE_TEACHERS to LOGIN_METHOD_VULCAN_HEBE + ENDPOINT_VULCAN_HEBE_ADDRESSBOOK_2 to LOGIN_METHOD_VULCAN_HEBE, + ENDPOINT_VULCAN_HEBE_TEACHERS to LOGIN_METHOD_VULCAN_HEBE, + ENDPOINT_VULCAN_HEBE_MESSAGE_BOXES to LOGIN_METHOD_VULCAN_HEBE, ), listOf(LOGIN_METHOD_VULCAN_HEBE)) ) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanData.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanData.kt index 6170d817..4b3f961f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanData.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanData.kt @@ -21,10 +21,12 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { ENDPOINT_VULCAN_HEBE_MAIN, ENDPOINT_VULCAN_HEBE_PUSH_CONFIG, ENDPOINT_VULCAN_HEBE_ADDRESSBOOK, + ENDPOINT_VULCAN_HEBE_ADDRESSBOOK_2, ENDPOINT_VULCAN_HEBE_TIMETABLE, ENDPOINT_VULCAN_HEBE_EXAMS, ENDPOINT_VULCAN_HEBE_HOMEWORK, ENDPOINT_VULCAN_HEBE_NOTICES, + ENDPOINT_VULCAN_HEBE_MESSAGE_BOXES, ENDPOINT_VULCAN_HEBE_MESSAGES_INBOX, ENDPOINT_VULCAN_HEBE_MESSAGES_SENT, ENDPOINT_VULCAN_HEBE_TEACHERS, @@ -107,6 +109,10 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_addressbook) VulcanHebeAddressbook(data, lastSync, onSuccess) } + ENDPOINT_VULCAN_HEBE_ADDRESSBOOK_2 -> { + data.startProgress(R.string.edziennik_progress_endpoint_addressbook) + VulcanHebeAddressbook2(data, lastSync, onSuccess) + } ENDPOINT_VULCAN_HEBE_TEACHERS -> { data.startProgress(R.string.edziennik_progress_endpoint_teachers) VulcanHebeTeachers(data, lastSync, onSuccess) @@ -139,6 +145,10 @@ class VulcanData(val data: DataVulcan, val onSuccess: () -> Unit) { data.startProgress(R.string.edziennik_progress_endpoint_attendance) VulcanHebeAttendance(data, lastSync, onSuccess) } + ENDPOINT_VULCAN_HEBE_MESSAGE_BOXES -> { + data.startProgress(R.string.edziennik_progress_endpoint_messages) + VulcanHebeMessageBoxes(data, lastSync, onSuccess) + } ENDPOINT_VULCAN_HEBE_MESSAGES_INBOX -> { data.startProgress(R.string.edziennik_progress_endpoint_messages_inbox) VulcanHebeMessages(data, lastSync, onSuccess).getMessages(Message.TYPE_RECEIVED) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanHebe.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanHebe.kt index 457306c2..a99ac7a4 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanHebe.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/VulcanHebe.kt @@ -14,7 +14,6 @@ import im.wangchao.mhttp.Response import im.wangchao.mhttp.body.MediaTypeUtils import im.wangchao.mhttp.callback.JsonCallbackHandler import io.github.wulkanowy.signer.hebe.getSignatureHeaders -import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.data.api.* import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.hebe.HebeFilterType @@ -55,6 +54,15 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) { return date.getLong("Timestamp") ?: return default } + fun buildDateTime(): JsonObject { + return JsonObject( + "Timestamp" to System.currentTimeMillis(), + "Date" to Date.getToday().stringY_m_d, + "DateDisplay" to Date.getToday().stringDmy, + "Time" to Time.getNow().stringHMS, + ) + } + fun getDate(json: JsonObject?, key: String): Date? { val date = json.getJsonObject(key) return date.getString("Date")?.let { Date.fromY_m_d(it) } @@ -74,6 +82,22 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) { return teacherId } + fun getTeacherRecipient(json: JsonObject): Teacher? { + val globalKey = json.getString("GlobalKey") ?: return null + if (globalKey == data.messageBoxKey) + return null + var name = json.getString("Name") ?: return null + val group = json.getString("Group", "P") + val loginId = "${globalKey};${group};${name}" + val pattern = " - $group - (${data.schoolShort})" + if (name.endsWith(pattern)) + name = name.substringBefore(pattern) + val teacher = data.getTeacherByFirstLast(name, loginId) + if (teacher.type == 0) + teacher.type = Teacher.TYPE_OTHER + return teacher + } + fun getSubjectId(json: JsonObject?, key: String): Long? { val subject = json.getJsonObject(key) val subjectId = subject.getLong("Id") ?: return null @@ -89,7 +113,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) { } fun getTeamId(json: JsonObject?, key: String): Long? { - val team = json.getJsonObject(key) + val team = json.getJsonObject(key) ?: return null val teamId = team.getLong("Id") var teamName = team.getString("Shortcut") ?: team.getString("Name") @@ -104,7 +128,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) { } fun getClassId(json: JsonObject?, key: String): Long? { - val team = json.getJsonObject(key) + val team = json.getJsonObject(key) ?: return null val teamId = team.getLong("Id") val teamName = data.profile?.studentClassName ?: team.getString("Name") @@ -148,7 +172,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) { fun isCurrentYear(dateTime: Long): Boolean { return profile?.let { profile -> - return@let dateTime >= profile.dateSemester1Start.inMillis + return@let dateTime >= profile.dateSemester1Start.inMillis - WEEK * MS } ?: false } @@ -355,6 +379,7 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) { dateTo: Date? = null, lastSync: Long? = null, folder: Int? = null, + messageBox: String? = null, params: Map = mapOf(), includeFilterType: Boolean = true, onSuccess: (data: List, response: Response?) -> Unit @@ -378,6 +403,9 @@ open class VulcanHebe(open val data: DataVulcan, open val lastSync: Long?) { query["periodId"] = data.studentSemesterId.toString() query["pupilId"] = data.studentId.toString() } + HebeFilterType.BY_MESSAGEBOX -> { + query["box"] = messageBox ?: data.messageBoxKey ?: "" + } } if (dateFrom != null) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/HebeFilterType.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/HebeFilterType.kt index a4ea84b1..bd8b58e5 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/HebeFilterType.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/HebeFilterType.kt @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.hebe enum class HebeFilterType(val endpoint: String) { + BY_MESSAGEBOX("byBox"), BY_PUPIL("byPupil"), BY_PERSON("byPerson"), BY_PERIOD("byPeriod") diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeAddressbook.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeAddressbook.kt index 13eeb8db..d7bec22b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeAddressbook.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeAddressbook.kt @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.hebe import androidx.core.util.set +import androidx.room.OnConflictStrategy import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.data.api.VULCAN_HEBE_ENDPOINT_ADDRESSBOOK import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan @@ -44,7 +45,6 @@ class VulcanHebeAddressbook( ) { list, _ -> list.forEach { person -> val id = person.getString("Id") ?: return@forEach - val loginId = person.getString("LoginId") ?: return@forEach val idType = id.split("-") .getOrNull(0) @@ -69,7 +69,7 @@ class VulcanHebeAddressbook( idLong, name, surname, - loginId + null ).also { data.teacherList[idLong] = it } @@ -108,13 +108,14 @@ class VulcanHebeAddressbook( } teacher.setTeacherType(personType) - teacher.typeDescription = roleText + if (roleText != null) + teacher.typeDescription = roleText } if (teacher.type == 0) teacher.setTeacherType(typeBase) } - + data.teacherOnConflictStrategy = OnConflictStrategy.REPLACE data.setSyncNext(ENDPOINT_VULCAN_HEBE_ADDRESSBOOK, 2 * DAY) onSuccess(ENDPOINT_VULCAN_HEBE_ADDRESSBOOK) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeAddressbook2.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeAddressbook2.kt new file mode 100644 index 00000000..1a6606d0 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeAddressbook2.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2021-2-21. + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.hebe + +import androidx.room.OnConflictStrategy +import pl.szczodrzynski.edziennik.data.api.VULCAN_HEBE_ENDPOINT_MESSAGEBOX_ADDRESSBOOK +import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_HEBE_ADDRESSBOOK_2 +import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanHebe +import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_OTHER +import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_PARENT +import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_STUDENT +import pl.szczodrzynski.edziennik.data.db.entity.Teacher.Companion.TYPE_TEACHER +import pl.szczodrzynski.edziennik.ext.DAY +import pl.szczodrzynski.edziennik.ext.getString + +class VulcanHebeAddressbook2( + override val data: DataVulcan, + override val lastSync: Long?, + val onSuccess: (endpointId: Int) -> Unit +) : VulcanHebe(data, lastSync) { + companion object { + const val TAG = "VulcanHebeAddressbook2" + } + + init { + apiGetList( + TAG, + VULCAN_HEBE_ENDPOINT_MESSAGEBOX_ADDRESSBOOK, + HebeFilterType.BY_MESSAGEBOX, + messageBox = data.messageBoxKey, + lastSync = lastSync, + includeFilterType = false + ) { list, _ -> + list.forEach { person -> + val teacher = getTeacherRecipient(person) ?: return@forEach + val group = person.getString("Group", "P") + if (teacher.type == TYPE_OTHER) { + teacher.type = when (group) { + "P" -> TYPE_TEACHER // Pracownik + "O" -> TYPE_PARENT // Opiekun + "U" -> TYPE_STUDENT // Uczeń + else -> TYPE_OTHER + } + } + } + data.teacherOnConflictStrategy = OnConflictStrategy.REPLACE + data.setSyncNext(ENDPOINT_VULCAN_HEBE_ADDRESSBOOK_2, 2 * DAY) + onSuccess(ENDPOINT_VULCAN_HEBE_ADDRESSBOOK_2) + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessageBoxes.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessageBoxes.kt new file mode 100644 index 00000000..e1c03aff --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessageBoxes.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2022-9-16. + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.hebe + +import pl.szczodrzynski.edziennik.data.api.VULCAN_HEBE_ENDPOINT_MESSAGEBOX +import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan +import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_HEBE_MESSAGE_BOXES +import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanHebe +import pl.szczodrzynski.edziennik.ext.DAY +import pl.szczodrzynski.edziennik.ext.getString + +class VulcanHebeMessageBoxes( + override val data: DataVulcan, + override val lastSync: Long?, + val onSuccess: (endpointId: Int) -> Unit +) : VulcanHebe(data, lastSync) { + companion object { + const val TAG = "VulcanHebeMessageBoxes" + } + + init { + apiGetList( + TAG, + VULCAN_HEBE_ENDPOINT_MESSAGEBOX, + lastSync = lastSync + ) { list, _ -> + for (messageBox in list) { + val name = messageBox.getString("Name") ?: continue + val studentName = profile?.studentNameLong ?: continue + if (!name.startsWith(studentName)) + continue + + data.messageBoxKey = messageBox.getString("GlobalKey") + data.messageBoxName = name + break + } + data.setSyncNext(ENDPOINT_VULCAN_HEBE_MESSAGE_BOXES, 7 * DAY) + onSuccess(ENDPOINT_VULCAN_HEBE_MESSAGE_BOXES) + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessages.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessages.kt index 5d6e5da4..29f37e62 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessages.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessages.kt @@ -4,22 +4,21 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.hebe -import androidx.core.util.set -import com.google.gson.JsonObject -import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.MainActivity.Companion.DRAWER_ITEM_MESSAGES -import pl.szczodrzynski.edziennik.data.api.VULCAN_HEBE_ENDPOINT_MESSAGES +import pl.szczodrzynski.edziennik.data.api.VULCAN_HEBE_ENDPOINT_MESSAGEBOX_MESSAGES import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_HEBE_MESSAGES_INBOX import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.ENDPOINT_VULCAN_HEBE_MESSAGES_SENT import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanHebe -import pl.szczodrzynski.edziennik.data.db.entity.* +import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_DELETED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_RECEIVED import pl.szczodrzynski.edziennik.data.db.entity.Message.Companion.TYPE_SENT +import pl.szczodrzynski.edziennik.data.db.entity.MessageRecipient +import pl.szczodrzynski.edziennik.data.db.entity.Metadata +import pl.szczodrzynski.edziennik.data.db.entity.SYNC_ALWAYS import pl.szczodrzynski.edziennik.ext.* import pl.szczodrzynski.edziennik.utils.Utils -import pl.szczodrzynski.navlib.crc16 class VulcanHebeMessages( override val data: DataVulcan, @@ -27,29 +26,7 @@ class VulcanHebeMessages( val onSuccess: (endpointId: Int) -> Unit ) : VulcanHebe(data, lastSync) { companion object { - const val TAG = "VulcanHebeMessagesInbox" - } - - private fun getPersonId(json: JsonObject): Long { - val senderLoginId = json.getInt("LoginId") ?: return -1 - /*if (senderLoginId == data.studentLoginId) - return -1*/ - - val senderName = json.getString("Address") ?: return -1 - val senderNameSplit = senderName.splitName() - val senderLoginIdStr = senderLoginId.toString() - val teacher = data.teacherList.singleOrNull { it.loginId == senderLoginIdStr } - ?: Teacher( - profileId, - -1 * crc16(senderName).toLong(), - senderNameSplit?.second ?: "", - senderNameSplit?.first ?: "", - senderLoginIdStr - ).also { - it.setTeacherType(Teacher.TYPE_OTHER) - data.teacherList[it.id] = it - } - return teacher.id + const val TAG = "VulcanHebeMessages" } fun getMessages(messageType: Int) { @@ -64,17 +41,28 @@ class VulcanHebeMessages( TYPE_SENT -> ENDPOINT_VULCAN_HEBE_MESSAGES_SENT else -> ENDPOINT_VULCAN_HEBE_MESSAGES_INBOX } + + val messageBox = data.messageBoxKey + if (messageBox == null) { + onSuccess(endpointId) + return + } + apiGetList( TAG, - VULCAN_HEBE_ENDPOINT_MESSAGES, - HebeFilterType.BY_PERSON, + VULCAN_HEBE_ENDPOINT_MESSAGEBOX_MESSAGES, + HebeFilterType.BY_MESSAGEBOX, + messageBox = data.messageBoxKey, folder = folder, lastSync = lastSync ) { list, _ -> list.forEach { message -> - val id = message.getLong("Id") ?: return@forEach + val uuid = message.getString("Id") ?: return@forEach + val id = Utils.crc32(uuid.toByteArray()) + val globalKey = message.getString("GlobalKey", "") + val threadKey = message.getString("ThreadKey", "") val subject = message.getString("Subject") ?: return@forEach - val body = message.getString("Content") ?: return@forEach + var body = message.getString("Content") ?: return@forEach val sender = message.getJsonObject("Sender") ?: return@forEach @@ -83,13 +71,27 @@ class VulcanHebeMessages( if (!isCurrentYear(sentDate)) return@forEach + val senderId = if (messageType == TYPE_RECEIVED) + getTeacherRecipient(sender)?.id + else + null + + val meta = mutableMapOf( + "uuid" to uuid, + "globalKey" to globalKey, + "threadKey" to threadKey, + ) + val metaString = meta.map { "${it.key}=${it.value}" }.join("&") + body = "[META:${metaString}]" + body + body = body.replace("\n", "
") + val messageObject = Message( profileId = profileId, id = id, type = messageType, subject = subject, - body = body.replace("\n", "
"), - senderId = if (messageType == TYPE_RECEIVED) getPersonId(sender) else null, + body = body, + senderId = senderId, addedDate = sentDate ) @@ -101,9 +103,14 @@ class VulcanHebeMessages( else -1 for (receiver in receivers) { + val recipientId = if (messageType == TYPE_SENT) + getTeacherRecipient(receiver)?.id ?: -1 + else + -1 + val messageRecipientObject = MessageRecipient( profileId, - if (messageType == TYPE_SENT) getPersonId(receiver) else -1, + recipientId, -1, receiverReadDate, id @@ -115,6 +122,9 @@ class VulcanHebeMessages( ?.asJsonObjectList() ?: return@forEach + messageObject.attachmentIds = mutableListOf() + messageObject.attachmentNames = mutableListOf() + messageObject.attachmentSizes = mutableListOf() for (attachment in attachments) { val fileName = attachment.getString("Name") ?: continue val url = attachment.getString("Link") ?: continue diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessagesChangeStatus.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessagesChangeStatus.kt index bd86cfce..f0090a9c 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessagesChangeStatus.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeMessagesChangeStatus.kt @@ -5,7 +5,7 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.hebe import org.greenrobot.eventbus.EventBus -import pl.szczodrzynski.edziennik.data.api.VULCAN_HEBE_ENDPOINT_MESSAGES_STATUS +import pl.szczodrzynski.edziennik.data.api.VULCAN_HEBE_ENDPOINT_MESSAGEBOX_STATUS import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanHebe import pl.szczodrzynski.edziennik.data.api.events.MessageGetEvent @@ -23,13 +23,19 @@ class VulcanHebeMessagesChangeStatus( const val TAG = "VulcanHebeMessagesChangeStatus" } - init { + init { let { + val messageKey = messageObject.body?.let { data.parseMessageMeta(it) }?.get("globalKey") ?: run { + EventBus.getDefault().postSticky(MessageGetEvent(messageObject)) + onSuccess() + return@let + } + apiPost( TAG, - VULCAN_HEBE_ENDPOINT_MESSAGES_STATUS, + VULCAN_HEBE_ENDPOINT_MESSAGEBOX_STATUS, payload = JsonObject( - "MessageId" to messageObject.id, - "LoginId" to data.studentLoginId, + "BoxKey" to data.messageBoxKey, + "MessageKey" to messageKey, "Status" to 1 ) ) { _: Boolean, _ -> @@ -61,5 +67,5 @@ class VulcanHebeMessagesChangeStatus( EventBus.getDefault().postSticky(MessageGetEvent(messageObject)) onSuccess() } - } + }} } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeSendMessage.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeSendMessage.kt index e71c4089..5e607caa 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeSendMessage.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeSendMessage.kt @@ -4,17 +4,20 @@ package pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.hebe +import com.google.gson.JsonArray import com.google.gson.JsonObject import org.greenrobot.eventbus.EventBus import pl.szczodrzynski.edziennik.* +import pl.szczodrzynski.edziennik.data.api.ERROR_MESSAGE_NOT_SENT import pl.szczodrzynski.edziennik.data.api.ERROR_VULCAN_HEBE_MISSING_SENDER_ENTRY -import pl.szczodrzynski.edziennik.data.api.VULCAN_HEBE_ENDPOINT_MESSAGES_SEND +import pl.szczodrzynski.edziennik.data.api.VULCAN_HEBE_ENDPOINT_MESSAGEBOX_SEND import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.DataVulcan import pl.szczodrzynski.edziennik.data.api.edziennik.vulcan.data.VulcanHebe import pl.szczodrzynski.edziennik.data.api.events.MessageSentEvent import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.ext.* +import java.util.UUID class VulcanHebeSendMessage( override val data: DataVulcan, @@ -28,9 +31,9 @@ class VulcanHebeSendMessage( } init { - if (data.senderAddressName == null || data.senderAddressHash == null) { - VulcanHebeMain(data).getStudents(data.profile, null) { - if (data.senderAddressName == null || data.senderAddressHash == null) { + if (data.messageBoxKey == null || data.messageBoxName == null) { + VulcanHebeMessageBoxes(data, 0) { + if (data.messageBoxKey == null || data.messageBoxName == null) { data.error(TAG, ERROR_VULCAN_HEBE_MISSING_SENDER_ENTRY) } else { @@ -44,47 +47,64 @@ class VulcanHebeSendMessage( } private fun sendMessage() { + val uuid = UUID.randomUUID().toString() + val globalKey = UUID.randomUUID().toString() + val partition = "${data.symbol}-${data.schoolSymbol}" + val recipientsArray = JsonArray() recipients.forEach { teacher -> + val loginId = teacher.loginId?.split(";", limit = 3) ?: return@forEach + val key = loginId.getOrNull(0) ?: teacher.loginId + val group = loginId.getOrNull(1) + val name = loginId.getOrNull(2) + if (key?.toIntOrNull() != null) { + // raise error for old-format (non-UUID) login IDs + data.error(TAG, ERROR_MESSAGE_NOT_SENT) + return + } recipientsArray += JsonObject( - "Address" to teacher.fullNameLastFirst, - "LoginId" to (teacher.loginId?.toIntOrNull() ?: return@forEach), - "Initials" to teacher.initialsLastFirst, - "AddressHash" to teacher.fullNameLastFirst.sha1Hex() + "Id" to "${data.messageBoxKey}-${key}", + "Partition" to partition, + "Owner" to data.messageBoxKey, + "GlobalKey" to key, + "Name" to name, + "Group" to group, + "Initials" to "", + "HasRead" to 0, ) } - val senderName = (profile?.accountName ?: profile?.studentNameLong) - ?.swapFirstLastName() ?: "" val sender = JsonObject( - "Address" to data.senderAddressName, - "LoginId" to data.studentLoginId.toString(), - "Initials" to senderName.getNameInitials(), - "AddressHash" to data.senderAddressHash + "Id" to "0", + "Partition" to partition, + "Owner" to data.messageBoxKey, + "GlobalKey" to data.messageBoxKey, + "Name" to data.messageBoxName, + "Group" to "", + "Initials" to "", + "HasRead" to 0, ) apiPost( TAG, - VULCAN_HEBE_ENDPOINT_MESSAGES_SEND, + VULCAN_HEBE_ENDPOINT_MESSAGEBOX_SEND, payload = JsonObject( - "Status" to 1, - "Sender" to sender, - "DateSent" to null, - "DateRead" to null, - "Content" to text, - "Receiver" to recipientsArray, - "Id" to 0, + "Id" to uuid, + "GlobalKey" to globalKey, + "Partition" to partition, + "ThreadKey" to globalKey, // TODO correct threadKey for reply messages "Subject" to subject, - "Attachments" to null, - "Self" to null + "Content" to text, + "Status" to 1, + "Owner" to data.messageBoxKey, + "DateSent" to buildDateTime(), + "DateRead" to null, + "Sender" to sender, + "Receiver" to recipientsArray, + "Attachments" to JsonArray(), ) - ) { json: JsonObject, _ -> - val messageId = json.getLong("Id") - - if (messageId == null) { - // TODO error - return@apiPost - } + ) { _: JsonObject, _ -> + // TODO handle errors VulcanHebeMessages(data, null) { val message = data.messageList.firstOrNull { it.isSent && it.subject == subject } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeTeachers.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeTeachers.kt index f01e0d6b..7be65e34 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeTeachers.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/data/hebe/VulcanHebeTeachers.kt @@ -45,6 +45,7 @@ class VulcanHebeTeachers( when (subjectName) { "Pedagog" -> teacher.setTeacherType(Teacher.TYPE_PEDAGOGUE) + "Dyrektor" -> teacher.setTeacherType(Teacher.TYPE_PRINCIPAL) else -> { val subjectId = data.getSubject(null, subjectName).id if (!teacher.subjects.contains(subjectId)) 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 94e12fe8..ea53112a 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 @@ -11,6 +11,7 @@ import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.BuildConfig import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.data.api.ERROR_REQUEST_FAILURE +import pl.szczodrzynski.edziennik.data.api.Regexes.MESSAGE_META import pl.szczodrzynski.edziennik.data.api.interfaces.EndpointCallback import pl.szczodrzynski.edziennik.data.db.AppDb import pl.szczodrzynski.edziennik.data.db.entity.* @@ -489,11 +490,19 @@ abstract class Data(val app: App, val profile: Profile?, val loginStore: LoginSt teacherList[id] = this } return obj.also { - if (loginId != null && it.loginId != null) + if (loginId != null) it.loginId = loginId if (firstName.length > 1) it.name = firstName it.surname = lastName } } + + fun parseMessageMeta(body: String): Map? { + val match = MESSAGE_META.find(body) ?: return null + return match[1].split("&").associateBy( + { it.substringBefore("=") }, + { it.substringAfter("=") }, + ) + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/messages/compose/MessagesComposeFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/messages/compose/MessagesComposeFragment.kt index 7266be41..52e7c23b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/messages/compose/MessagesComposeFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/messages/compose/MessagesComposeFragment.kt @@ -77,7 +77,7 @@ class MessagesComposeFragment : Fragment(), CoroutineScope { private lateinit var stylingConfig: StylingConfig private lateinit var uiConfig: UIConfig private val enableTextStyling - get() = app.profile.loginStoreType != LoginStore.LOGIN_TYPE_VULCAN && app.profile.loginStoreType != LoginStore.LOGIN_TYPE_LIBRUS + get() = app.profile.loginStoreType != LoginStore.LOGIN_TYPE_LIBRUS private var changedRecipients = false private var changedSubject = false private var changedBody = false diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/BetterHtml.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/BetterHtml.kt index 2e3bc072..97610845 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/BetterHtml.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/html/BetterHtml.kt @@ -15,6 +15,7 @@ import android.text.style.* import androidx.appcompat.widget.AppCompatEditText import androidx.core.graphics.ColorUtils import androidx.core.text.HtmlCompat +import pl.szczodrzynski.edziennik.data.api.Regexes.MESSAGE_META import pl.szczodrzynski.edziennik.ext.dp import pl.szczodrzynski.edziennik.ext.getWordBounds import pl.szczodrzynski.edziennik.ext.resolveAttr @@ -45,7 +46,7 @@ object BetterHtml { .toRegex(RegexOption.IGNORE_CASE) var text = html - .replace("\\[META:[A-z0-9]+;[0-9-]+]".toRegex(), "") + .replace(MESSAGE_META, "") .replace("background-color: ?$hexPattern;".toRegex(), "") // treat paragraphs as if they had no margin .replace("Open-source licenses Privacy policy E-register - © Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 - 2022 + © Kuba Szczodrzyński && Kacper Ziubryniewicz\nSeptember 2018 – 2022 Click to check for updates Update Version diff --git a/build.gradle b/build.gradle index c259c51b..c9b3fbae 100644 --- a/build.gradle +++ b/build.gradle @@ -23,8 +23,8 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:7.2.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.google.gms:google-services:4.3.13' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1' + classpath 'com.google.gms:google-services:4.3.14' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' } }