From 93034834707db98738c1c524272a31f71a200b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Wed, 1 Apr 2020 17:07:50 +0200 Subject: [PATCH] [API] Implement Mobidziennik homework attachment downloading. Modify the interface a bit. Show attachments in UI. --- .../pl/szczodrzynski/edziennik/Extensions.kt | 7 + .../data/api/edziennik/EdziennikTask.kt | 7 +- .../api/edziennik/edudziennik/Edudziennik.kt | 3 +- .../data/api/edziennik/idziennik/Idziennik.kt | 5 +- .../data/web/IdziennikWebGetAttachment.kt | 10 +- .../data/api/edziennik/librus/Librus.kt | 5 +- .../messages/LibrusMessagesGetAttachment.kt | 10 +- .../edziennik/mobidziennik/Mobidziennik.kt | 13 +- .../data/web/MobidziennikWebGetAttachment.kt | 35 +++- .../data/web/MobidziennikWebGetHomework.kt | 57 +++++++ .../data/api/edziennik/template/Template.kt | 3 +- .../data/api/edziennik/vulcan/Vulcan.kt | 3 +- .../data/api/events/AttachmentGetEvent.kt | 12 +- .../data/api/interfaces/EdziennikInterface.kt | 3 +- .../ui/dialogs/event/EventDetailsDialog.kt | 23 +++ .../ui/modules/messages/MessageFragment.kt | 2 +- .../ui/modules/views/AttachmentAdapter.kt | 125 ++++++++++++++ .../ui/modules/views/AttachmentsView.kt | 159 ++++++++++++++++++ .../main/res/layout/attachment_list_item.xml | 36 ++++ .../main/res/layout/dialog_event_details.xml | 18 +- app/src/main/res/values/strings.xml | 1 + 21 files changed, 487 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetHomework.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/AttachmentAdapter.kt create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/AttachmentsView.kt create mode 100644 app/src/main/res/layout/attachment_list_item.xml diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt index 707c8d1b..1a51ddbd 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/Extensions.kt @@ -165,6 +165,13 @@ fun Bundle?.getString(key: String, defaultValue: String): String { return this?.getString(key, defaultValue) ?: defaultValue } +fun Bundle?.getIntOrNull(key: String): Int? { + return this?.get(key) as? Int +} +fun Bundle?.get(key: String): T? { + return this?.get(key) as? T? +} + /** * ` The quick BROWN_fox Jumps OveR THE LAZy-DOG. ` * diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt index 773db107..13de2f17 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/EdziennikTask.kt @@ -19,7 +19,6 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.db.entity.LoginStore -import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.EventFull @@ -37,7 +36,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa fun messageSend(profileId: Int, recipients: List, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text)) fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest()) fun announcementGet(profileId: Int, announcement: AnnouncementFull) = EdziennikTask(profileId, AnnouncementGetRequest(announcement)) - fun attachmentGet(profileId: Int, message: Message, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(message, attachmentId, attachmentName)) + fun attachmentGet(profileId: Int, owner: Any, attachmentId: Long, attachmentName: String) = EdziennikTask(profileId, AttachmentGetRequest(owner, attachmentId, attachmentName)) fun recipientListGet(profileId: Int) = EdziennikTask(profileId, RecipientListGetRequest()) fun eventGet(profileId: Int, event: EventFull) = EdziennikTask(profileId, EventGetRequest(event)) } @@ -94,7 +93,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa is FirstLoginRequest -> edziennikInterface?.firstLogin() is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead() is AnnouncementGetRequest -> edziennikInterface?.getAnnouncement(request.announcement) - is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.message, request.attachmentId, request.attachmentName) + is AttachmentGetRequest -> edziennikInterface?.getAttachment(request.owner, request.attachmentId, request.attachmentName) is RecipientListGetRequest -> edziennikInterface?.getRecipientList() is EventGetRequest -> edziennikInterface?.getEvent(request.event) } @@ -116,7 +115,7 @@ open class EdziennikTask(override val profileId: Int, val request: Any) : IApiTa data class MessageSendRequest(val recipients: List, val subject: String, val text: String) class AnnouncementsReadRequest data class AnnouncementGetRequest(val announcement: AnnouncementFull) - data class AttachmentGetRequest(val message: Message, val attachmentId: Long, val attachmentName: String) + data class AttachmentGetRequest(val owner: Any, val attachmentId: Long, val attachmentName: String) class RecipientListGetRequest data class EventGetRequest(val event: EventFull) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/Edudziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/Edudziennik.kt index 561c94c8..3119c328 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/Edudziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/edudziennik/Edudziennik.kt @@ -16,7 +16,6 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.db.entity.LoginStore -import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull @@ -95,7 +94,7 @@ class Edudziennik(val app: App, val profile: Profile?, val loginStore: LoginStor } } - override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {} + override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {} override fun getRecipientList() {} override fun getEvent(eventFull: EventFull) {} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/Idziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/Idziennik.kt index a698ed69..3a279eff 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/Idziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/Idziennik.kt @@ -18,7 +18,6 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.db.entity.LoginStore -import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull @@ -104,9 +103,9 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, override fun markAllAnnouncementsAsRead() {} override fun getAnnouncement(announcement: AnnouncementFull) {} - override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) { + override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) { login(LOGIN_METHOD_IDZIENNIK_WEB) { - IdziennikWebGetAttachment(data, message, attachmentId, attachmentName) { + IdziennikWebGetAttachment(data, owner, attachmentId, attachmentName) { completed() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetAttachment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetAttachment.kt index 562f527c..86cb2eca 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetAttachment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/idziennik/data/web/IdziennikWebGetAttachment.kt @@ -15,7 +15,7 @@ import pl.szczodrzynski.edziennik.utils.Utils import java.io.File class IdziennikWebGetAttachment(override val data: DataIdziennik, - val message: Message, + val owner: Any, val attachmentId: Long, val attachmentName: String, val onSuccess: () -> Unit @@ -25,6 +25,8 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik, } init { + val message = owner as Message + val messageId = "\\[META:([A-z0-9]+);([0-9-]+)]".toRegex().find(message.body ?: "")?.get(2) ?: -1 val targetFile = File(Utils.getStorageDir(), attachmentName) @@ -34,13 +36,13 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik, ), { file -> val event = AttachmentGetEvent( profileId, - message.id, + owner, attachmentId, AttachmentGetEvent.TYPE_FINISHED, file.absolutePath ) - val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}") + val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}") Utils.writeStringToFile(attachmentDataFile, event.fileName) EventBus.getDefault().post(event) @@ -50,7 +52,7 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik, }) { written, _ -> val event = AttachmentGetEvent( profileId, - message.id, + owner, attachmentId, AttachmentGetEvent.TYPE_PROGRESS, bytesWritten = written 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 7995a905..5a01bb7b 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 @@ -20,7 +20,6 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.db.entity.LoginStore -import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull @@ -119,9 +118,9 @@ class Librus(val app: App, val profile: Profile?, val loginStore: LoginStore, va } } - override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) { + override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) { login(LOGIN_METHOD_LIBRUS_MESSAGES) { - LibrusMessagesGetAttachment(data, message, attachmentId, attachmentName) { + LibrusMessagesGetAttachment(data, owner, attachmentId, attachmentName) { completed() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetAttachment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetAttachment.kt index 30031684..83f6ebc1 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetAttachment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/librus/data/messages/LibrusMessagesGetAttachment.kt @@ -21,7 +21,7 @@ import java.io.File import kotlin.coroutines.CoroutineContext class LibrusMessagesGetAttachment(override val data: DataLibrus, - val message: Message, + val owner: Any, val attachmentId: Long, val attachmentName: String, val onSuccess: () -> Unit @@ -38,6 +38,8 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus, private var getAttachmentCheckKeyTries = 0 init { + val message = owner as Message + messagesGet(TAG, "GetFileDownloadLink", parameters = mapOf( "fileId" to attachmentId, "msgId" to message.id, @@ -95,13 +97,13 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus, val event = AttachmentGetEvent( profileId, - message.id, + owner, attachmentId, TYPE_FINISHED, file.absolutePath ) - val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}") + val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}") Utils.writeStringToFile(attachmentDataFile, event.fileName) EventBus.getDefault().post(event) @@ -111,7 +113,7 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus, }) { written, _ -> val event = AttachmentGetEvent( profileId, - message.id, + owner, attachmentId, TYPE_PROGRESS, bytesWritten = written diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/Mobidziennik.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/Mobidziennik.kt index 2d90c891..ae5c6072 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/Mobidziennik.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/Mobidziennik.kt @@ -15,14 +15,12 @@ import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikCallback import pl.szczodrzynski.edziennik.data.api.interfaces.EdziennikInterface import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.db.entity.LoginStore -import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.utils.Utils.d -import pl.szczodrzynski.edziennik.utils.models.Date class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface { companion object { @@ -104,9 +102,9 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto override fun markAllAnnouncementsAsRead() {} override fun getAnnouncement(announcement: AnnouncementFull) {} - override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) { + override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) { login(LOGIN_METHOD_MOBIDZIENNIK_WEB) { - MobidziennikWebGetAttachment(data, message, attachmentId, attachmentName) { + MobidziennikWebGetAttachment(data, owner, attachmentId, attachmentName) { completed() } } @@ -121,13 +119,8 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto } override fun getEvent(eventFull: EventFull) { - val type = if (eventFull.date >= Date.getToday()) - MobidziennikWebHomework.TYPE_CURRENT - else - MobidziennikWebHomework.TYPE_PAST - login(LOGIN_METHOD_MOBIDZIENNIK_WEB) { - MobidziennikWebHomework(data, 0L, type, eventFull) { + MobidziennikWebGetHomework(data, eventFull) { completed() } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetAttachment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetAttachment.kt index 80803441..a2f1b2f6 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetAttachment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetAttachment.kt @@ -8,12 +8,14 @@ import org.greenrobot.eventbus.EventBus import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent +import pl.szczodrzynski.edziennik.data.db.entity.Event import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.edziennik.utils.models.Date import java.io.File class MobidziennikWebGetAttachment(override val data: DataMobidziennik, - val message: Message, + val owner: Any, val attachmentId: Long, val attachmentName: String, val onSuccess: () -> Unit @@ -25,22 +27,37 @@ class MobidziennikWebGetAttachment(override val data: DataMobidziennik, init { val targetFile = File(Utils.getStorageDir(), attachmentName) - val typeUrl = if (message.type == Message.TYPE_SENT) - "wiadwyslana" - else - "wiadodebrana" + val typeUrl = when (owner) { + is Message -> if (owner.type == Message.TYPE_SENT) + "dziennik/wiadwyslana?id=" + else + "dziennik/wiadodebrana?id=" - webGetFile(TAG, "/dziennik/$typeUrl/?id=${message.id}&zalacznik=$attachmentId", targetFile, { file -> + is Event -> if (owner.date >= Date.getToday()) + "mobile/zadaniadomowe?id_zadania=" + else + "mobile/zadaniadomowearchiwalne?id_zadania=" + + else -> "" + } + + val ownerId = when (owner) { + is Message -> owner.id + is Event -> owner.id + else -> -1 + } + + webGetFile(TAG, "/$typeUrl${ownerId}&zalacznik=$attachmentId", targetFile, { file -> val event = AttachmentGetEvent( profileId, - message.id, + owner, attachmentId, AttachmentGetEvent.TYPE_FINISHED, file.absolutePath ) - val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.messageId}_${event.attachmentId}") + val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}") Utils.writeStringToFile(attachmentDataFile, event.fileName) EventBus.getDefault().post(event) @@ -51,7 +68,7 @@ class MobidziennikWebGetAttachment(override val data: DataMobidziennik, // TODO make use of bytesTotal val event = AttachmentGetEvent( profileId, - message.id, + owner, attachmentId, AttachmentGetEvent.TYPE_PROGRESS, bytesWritten = written diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetHomework.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetHomework.kt new file mode 100644 index 00000000..b940a205 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebGetHomework.kt @@ -0,0 +1,57 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-3-31. + */ + +package pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.web + +import org.greenrobot.eventbus.EventBus +import pl.szczodrzynski.edziennik.data.api.Regexes +import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.DataMobidziennik +import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb +import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent +import pl.szczodrzynski.edziennik.data.db.full.EventFull +import pl.szczodrzynski.edziennik.get +import pl.szczodrzynski.edziennik.utils.models.Date + +class MobidziennikWebGetHomework(override val data: DataMobidziennik, + val event: EventFull, + val onSuccess: () -> Unit +) : MobidziennikWeb(data, null) { + companion object { + private const val TAG = "MobidziennikWebHomework" + } + + init { + val endpoint = if (event.date >= Date.getToday()) + "zadaniadomowe" + else + "zadaniadomowearchiwalne" + + webGet(TAG, "/mobile/$endpoint") { text -> + MobidziennikLuckyNumberExtractor(data, text) + + Regexes.MOBIDZIENNIK_HOMEWORK_ROW.findAll(text).forEach { homeworkMatch -> + val tableRow = homeworkMatch[1].ifBlank { return@forEach } + + val id = Regexes.MOBIDZIENNIK_HOMEWORK_ID.find(tableRow)?.get(1)?.toLongOrNull() ?: return@forEach + if (event.id != id) + return@forEach + + event.attachmentIds = mutableListOf() + event.attachmentNames = mutableListOf() + Regexes.MOBIDZIENNIK_HOMEWORK_ATTACHMENT.findAll(tableRow).forEach { + event.attachmentIds?.add(it[1].toLongOrNull() ?: return@forEach) + event.attachmentNames?.add(it[2]) + } + + event.homeworkBody = "" + } + + data.eventList.add(event) + data.eventListReplace = true + + EventBus.getDefault().postSticky(EventGetEvent(event)) + onSuccess() + } + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/template/Template.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/template/Template.kt index 49c8bbad..5843f927 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/template/Template.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/template/Template.kt @@ -16,7 +16,6 @@ import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.prepare import pl.szczodrzynski.edziennik.data.api.templateLoginMethods import pl.szczodrzynski.edziennik.data.db.entity.LoginStore -import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull @@ -80,7 +79,7 @@ class Template(val app: App, val profile: Profile?, val loginStore: LoginStore, } - override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) { + override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) { } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/Vulcan.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/Vulcan.kt index 208d65bf..511b6591 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/Vulcan.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/vulcan/Vulcan.kt @@ -19,7 +19,6 @@ import pl.szczodrzynski.edziennik.data.api.prepare import pl.szczodrzynski.edziennik.data.api.prepareFor import pl.szczodrzynski.edziennik.data.api.vulcanLoginMethods import pl.szczodrzynski.edziennik.data.db.entity.LoginStore -import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Profile import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull @@ -104,7 +103,7 @@ class Vulcan(val app: App, val profile: Profile?, val loginStore: LoginStore, va override fun markAllAnnouncementsAsRead() {} override fun getAnnouncement(announcement: AnnouncementFull) {} - override fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) {} + override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {} override fun getRecipientList() {} override fun getEvent(eventFull: EventFull) {} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/events/AttachmentGetEvent.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/events/AttachmentGetEvent.kt index cbc85b7d..52415c56 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/events/AttachmentGetEvent.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/events/AttachmentGetEvent.kt @@ -4,11 +4,21 @@ package pl.szczodrzynski.edziennik.data.api.events -data class AttachmentGetEvent(val profileId: Int, val messageId: Long, val attachmentId: Long, +import pl.szczodrzynski.edziennik.data.db.entity.Event +import pl.szczodrzynski.edziennik.data.db.entity.Message + +data class AttachmentGetEvent(val profileId: Int, val owner: Any, val attachmentId: Long, var eventType: Int = TYPE_PROGRESS, val fileName: String? = null, val bytesWritten: Long = 0) { companion object { const val TYPE_PROGRESS = 0 const val TYPE_FINISHED = 1 } + + val ownerId + get() = when (owner) { + is Message -> owner.id + is Event -> owner.id + else -> -1 + } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/interfaces/EdziennikInterface.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/interfaces/EdziennikInterface.kt index 00a292ae..153bbe18 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/interfaces/EdziennikInterface.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/interfaces/EdziennikInterface.kt @@ -5,7 +5,6 @@ package pl.szczodrzynski.edziennik.data.api.interfaces import com.google.gson.JsonObject -import pl.szczodrzynski.edziennik.data.db.entity.Message import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.EventFull @@ -17,7 +16,7 @@ interface EdziennikInterface { fun sendMessage(recipients: List, subject: String, text: String) fun markAllAnnouncementsAsRead() fun getAnnouncement(announcement: AnnouncementFull) - fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) + fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) fun getRecipientList() fun getEvent(eventFull: EventFull) fun firstLogin() diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventDetailsDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventDetailsDialog.kt index cbcdaaf8..6bda72e2 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventDetailsDialog.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/event/EventDetailsDialog.kt @@ -61,6 +61,7 @@ class EventDetailsDialog( if (activity.isFinishing) return@run onShowListener?.invoke(TAG) + EventBus.getDefault().register(this) app = activity.applicationContext as App b = DialogEventDetailsBinding.inflate(activity.layoutInflater) dialog = MaterialAlertDialogBuilder(activity) @@ -74,6 +75,7 @@ class EventDetailsDialog( } .setOnDismissListener { onDismissListener?.invoke(TAG) + EventBus.getDefault().unregister(this@EventDetailsDialog) progressDialog?.dismiss() } .show() @@ -197,11 +199,18 @@ class EventDetailsDialog( } if (event.homeworkBody == null && !event.addedManually && event.type == Event.TYPE_HOMEWORK) { + b.bodyTitle.isVisible = true b.bodyProgressBar.isVisible = true b.body.isVisible = false EdziennikTask.eventGet(event.profileId, event).enqueue(activity) } + else if (event.homeworkBody.isNullOrBlank()) { + b.bodyTitle.isVisible = false + b.bodyProgressBar.isVisible = false + b.body.isVisible = false + } else { + b.bodyTitle.isVisible = true b.bodyProgressBar.isVisible = false b.body.isVisible = true b.body.text = event.homeworkBody @@ -209,6 +218,20 @@ class EventDetailsDialog( dialog.dismiss() } } + + if (event.attachmentIds.isNullOrEmpty() || event.attachmentNames.isNullOrEmpty()) { + b.attachmentsTitle.isVisible = false + b.attachmentsFragment.isVisible = false + } + else { + b.attachmentsTitle.isVisible = true + b.attachmentsFragment.isVisible = true + b.attachmentsFragment.init(Bundle().also { + it.putInt("profileId", event.profileId) + it.putLongArray("attachmentIds", event.attachmentIds!!.toLongArray()) + it.putStringArray("attachmentNames", event.attachmentNames!!.toTypedArray()) + }, owner = event) + } } @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessageFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessageFragment.kt index 7a22850f..0468d46d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessageFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/messages/MessageFragment.kt @@ -375,7 +375,7 @@ class MessageFragment : Fragment(), CoroutineScope { @Subscribe(threadMode = ThreadMode.MAIN) fun onAttachmentGetEvent(event: AttachmentGetEvent) { attachmentList.firstOrNull { it.profileId == event.profileId - && it.messageId == event.messageId + && it.messageId == event.ownerId && it.attachmentId == event.attachmentId }?.let { attachment -> when (event.eventType) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/AttachmentAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/AttachmentAdapter.kt new file mode 100644 index 00000000..ebb61dd4 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/AttachmentAdapter.kt @@ -0,0 +1,125 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-1. + */ + +package pl.szczodrzynski.edziennik.ui.modules.views + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.chip.Chip +import com.mikepenz.iconics.IconicsDrawable +import com.mikepenz.iconics.typeface.IIcon +import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial +import com.mikepenz.iconics.typeface.library.szkolny.font.SzkolnyFont +import com.mikepenz.iconics.utils.sizeDp +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.entity.Event +import pl.szczodrzynski.edziennik.data.db.entity.Message +import pl.szczodrzynski.edziennik.databinding.AttachmentListItemBinding +import pl.szczodrzynski.edziennik.onClick +import pl.szczodrzynski.edziennik.onLongClick +import pl.szczodrzynski.edziennik.utils.Utils +import pl.szczodrzynski.navlib.colorAttr +import kotlin.coroutines.CoroutineContext + +class AttachmentAdapter( + val context: Context, + val onAttachmentClick: (item: Item) -> Unit, + val onAttachmentLongClick: ((view: Chip, item: Item) -> Unit)? = null +) : RecyclerView.Adapter(), CoroutineScope { + companion object { + private const val TAG = "AttachmentAdapter" + } + + private val app = context.applicationContext as App + // optional: place the manager here + + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + + var items = listOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val inflater = LayoutInflater.from(parent.context) + val view = AttachmentListItemBinding.inflate(inflater, parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = items[position] + val b = holder.b + + // create an icon for the attachment + val icon: IIcon = when (Utils.getExtensionFromFileName(item.name)) { + "doc", "docx", "odt", "rtf" -> SzkolnyFont.Icon.szf_file_word_outline + "xls", "xlsx", "ods" -> SzkolnyFont.Icon.szf_file_excel_outline + "ppt", "pptx", "odp" -> SzkolnyFont.Icon.szf_file_powerpoint_outline + "pdf" -> SzkolnyFont.Icon.szf_file_pdf_outline + "mp3", "wav", "aac" -> SzkolnyFont.Icon.szf_file_music_outline + "mp4", "avi", "3gp", "mkv", "flv" -> SzkolnyFont.Icon.szf_file_video_outline + "jpg", "jpeg", "png", "bmp", "gif" -> SzkolnyFont.Icon.szf_file_image_outline + "zip", "rar", "tar", "7z" -> SzkolnyFont.Icon.szf_zip_box_outline + "html", "cpp", "c", "h", "css", "java", "py" -> SzkolnyFont.Icon.szf_file_code_outline + else -> CommunityMaterial.Icon.cmd_file_document_outline + } + + b.chip.text = if (item.isDownloading) { + app.getString(R.string.messages_attachment_downloading_format, item.name, item.downloadProgress) + } + else { + item.size?.let { + app.getString(R.string.messages_attachment_format, item.name, Utils.readableFileSize(it)) + } ?: item.name + } + + b.chip.chipIcon = IconicsDrawable(context) + .icon(icon) + .colorAttr(context, R.attr.colorOnSurface) + .sizeDp(24) + b.chip.closeIcon = IconicsDrawable(context) + .icon(CommunityMaterial.Icon.cmd_check) + .colorAttr(context, R.attr.colorOnSurface) + .sizeDp(18) + + b.chip.isCloseIconVisible = item.isDownloaded && !item.isDownloading + // prevent progress bar flickering + if (b.progressBar.isVisible != item.isDownloading) + b.progressBar.isVisible = item.isDownloading + + b.chip.onClick { onAttachmentClick(item) } + onAttachmentLongClick?.let { listener -> + b.chip.onLongClick { listener(it, item); true } + } + } + + override fun getItemCount() = items.size + + data class Item( + val profileId: Int, + val owner: Any, + val id: Long, + val name: String, + var size: Long? + ) { + val ownerId + get() = when (owner) { + is Message -> owner.id + is Event -> owner.id + else -> -1 + } + var isDownloaded = false + var isDownloading = false + var downloadProgress: Float = 0f + var downloadedName: String? = null + } + + class ViewHolder(val b: AttachmentListItemBinding) : RecyclerView.ViewHolder(b.root) +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/AttachmentsView.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/AttachmentsView.kt new file mode 100644 index 00000000..c14b8b69 --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/views/AttachmentsView.kt @@ -0,0 +1,159 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2020-4-1. + */ + +package pl.szczodrzynski.edziennik.ui.modules.views + +import android.content.Context +import android.os.Bundle +import android.os.Environment +import android.util.AttributeSet +import androidx.appcompat.widget.PopupMenu +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.data.api.edziennik.EdziennikTask +import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent +import pl.szczodrzynski.edziennik.get +import pl.szczodrzynski.edziennik.isNotNullNorEmpty +import pl.szczodrzynski.edziennik.utils.SimpleDividerItemDecoration +import pl.szczodrzynski.edziennik.utils.Utils +import java.io.File + +class AttachmentsView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : RecyclerView(context, attrs, defStyleAttr) { + companion object { + private const val TAG = "AttachmentsFragment" + const val TYPE_MESSAGE = 0 + const val TYPE_EVENT = 1 + } + + private val storageDir by lazy { + val storageDir = Environment.getExternalStoragePublicDirectory("Szkolny.eu") + storageDir.mkdirs() + storageDir + } + + fun init(arguments: Bundle, owner: Any) { + val list = this as? RecyclerView ?: return + + val profileId = arguments.get("profileId") ?: return + val attachmentIds = arguments.getLongArray("attachmentIds") ?: return + val attachmentNames = arguments.getStringArray("attachmentNames") ?: return + val attachmentSizes = arguments.getLongArray("attachmentSizes") + + val adapter = AttachmentAdapter(context, onAttachmentClick = { item -> + downloadAttachment(item) + }, onAttachmentLongClick = { chip, item -> + val popupMenu = PopupMenu(chip.context, chip) + popupMenu.menu.add(0, 1, 0, R.string.messages_attachment_download_again) + popupMenu.setOnMenuItemClickListener { + downloadAttachment(item, forceDownload = true) + true + } + popupMenu.show() + }) + + attachmentIds.forEachIndexed { index, id -> + val name = attachmentNames[index] ?: return@forEachIndexed + val size = attachmentSizes?.getOrNull(index) + + val item = AttachmentAdapter.Item(profileId, owner, id, name, size) + adapter.items += item + checkAttachment(item = item) + } + + // load & configure the adapter + if (adapter.items.isNotNullNorEmpty() && list.adapter == null) { + list.adapter = adapter + list.apply { + setHasFixedSize(false) + layoutManager = LinearLayoutManager(context) + addItemDecoration(SimpleDividerItemDecoration(context)) + } + } + } + + private fun checkAttachment(item: AttachmentAdapter.Item): Boolean { + val attachmentDataFile = File(storageDir, "." + item.profileId + "_" + item.ownerId + "_" + item.id) + item.isDownloaded = if (attachmentDataFile.exists()) { + try { + val attachmentFileName = Utils.getStringFromFile(attachmentDataFile) + val attachmentFile = File(attachmentFileName) + attachmentFile.exists() + } catch (e: Exception) { + e.printStackTrace() + false + } + } else false + return item.isDownloaded + } + + private fun downloadAttachment(attachment: AttachmentAdapter.Item, forceDownload: Boolean = false) { + if (!forceDownload && attachment.isDownloaded) { + Utils.openFile(context, File(attachment.downloadedName)) + return + } + + attachment.isDownloading = true + (adapter as? AttachmentAdapter)?.let { + it.notifyItemChanged(it.items.indexOf(attachment)) + } + + EdziennikTask.attachmentGet( + attachment.profileId, + attachment.owner, + attachment.id, + attachment.name + ).enqueue(context) + } + + private val lastUpdate: Long = 0 + @Subscribe(threadMode = ThreadMode.MAIN) + fun onAttachmentGetEvent(event: AttachmentGetEvent) { + val attachment = (adapter as? AttachmentAdapter)?.items?.firstOrNull { + it.profileId == event.profileId + && it.owner == event.owner + && it.id == event.attachmentId + } ?: return + + + when (event.eventType) { + AttachmentGetEvent.TYPE_FINISHED -> { + // save the downloaded file name + attachment.downloadedName = event.fileName + attachment.isDownloading = false + attachment.isDownloaded = true + + // open the file + Utils.openFile(context, File(attachment.downloadedName)) + } + + AttachmentGetEvent.TYPE_PROGRESS -> { + attachment.downloadProgress = event.bytesWritten.toFloat() / 1000000f + } + } + + if (event.eventType != AttachmentGetEvent.TYPE_PROGRESS || System.currentTimeMillis() - lastUpdate > 100L) { + (adapter as? AttachmentAdapter)?.let { + it.notifyItemChanged(it.items.indexOf(attachment)) + } + } + } + + override fun onAttachedToWindow() { + EventBus.getDefault().register(this) + super.onAttachedToWindow() + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + EventBus.getDefault().unregister(this) + } +} diff --git a/app/src/main/res/layout/attachment_list_item.xml b/app/src/main/res/layout/attachment_list_item.xml new file mode 100644 index 00000000..4e8fcab0 --- /dev/null +++ b/app/src/main/res/layout/attachment_list_item.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_event_details.xml b/app/src/main/res/layout/dialog_event_details.xml index 03f69a7b..d31565ef 100644 --- a/app/src/main/res/layout/dialog_event_details.xml +++ b/app/src/main/res/layout/dialog_event_details.xml @@ -152,12 +152,12 @@ tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia." /> + android:textAppearance="@style/NavView.TextView.Helper" /> + + + + Oznacz jako wykonane Czy chcesz oznaczyć to zadanie jako wykonane?\n\nNie będzie ono się wyświetlać na stronie głównej oraz w aktualnych zadaniach domowych. Będzie wciąż dostępne w Terminarzu. Treść + Załączniki