[API] Implement Mobidziennik homework attachment downloading. Modify the interface a bit. Show attachments in UI.

This commit is contained in:
Kuba Szczodrzyński 2020-04-01 17:07:50 +02:00
parent f8adc86a0e
commit 9303483470
21 changed files with 487 additions and 50 deletions

View File

@ -165,6 +165,13 @@ fun Bundle?.getString(key: String, defaultValue: String): String {
return this?.getString(key, defaultValue) ?: defaultValue return this?.getString(key, defaultValue) ?: defaultValue
} }
fun Bundle?.getIntOrNull(key: String): Int? {
return this?.get(key) as? Int
}
fun <T : Any> Bundle?.get(key: String): T? {
return this?.get(key) as? T?
}
/** /**
* ` The quick BROWN_fox Jumps OveR THE LAZy-DOG. ` * ` The quick BROWN_fox Jumps OveR THE LAZy-DOG. `
* *

View File

@ -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.models.ApiError
import pl.szczodrzynski.edziennik.data.api.task.IApiTask import pl.szczodrzynski.edziennik.data.api.task.IApiTask
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore 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.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull 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<Teacher>, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text)) fun messageSend(profileId: Int, recipients: List<Teacher>, subject: String, text: String) = EdziennikTask(profileId, MessageSendRequest(recipients, subject, text))
fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest()) fun announcementsRead(profileId: Int) = EdziennikTask(profileId, AnnouncementsReadRequest())
fun announcementGet(profileId: Int, announcement: AnnouncementFull) = EdziennikTask(profileId, AnnouncementGetRequest(announcement)) 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 recipientListGet(profileId: Int) = EdziennikTask(profileId, RecipientListGetRequest())
fun eventGet(profileId: Int, event: EventFull) = EdziennikTask(profileId, EventGetRequest(event)) 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 FirstLoginRequest -> edziennikInterface?.firstLogin()
is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead() is AnnouncementsReadRequest -> edziennikInterface?.markAllAnnouncementsAsRead()
is AnnouncementGetRequest -> edziennikInterface?.getAnnouncement(request.announcement) 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 RecipientListGetRequest -> edziennikInterface?.getRecipientList()
is EventGetRequest -> edziennikInterface?.getEvent(request.event) 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<Teacher>, val subject: String, val text: String) data class MessageSendRequest(val recipients: List<Teacher>, val subject: String, val text: String)
class AnnouncementsReadRequest class AnnouncementsReadRequest
data class AnnouncementGetRequest(val announcement: AnnouncementFull) 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 class RecipientListGetRequest
data class EventGetRequest(val event: EventFull) data class EventGetRequest(val event: EventFull)
} }

View File

@ -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.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore 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.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull 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 getRecipientList() {}
override fun getEvent(eventFull: EventFull) {} override fun getEvent(eventFull: EventFull) {}

View File

@ -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.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore 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.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull 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 markAllAnnouncementsAsRead() {}
override fun getAnnouncement(announcement: AnnouncementFull) {} 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) { login(LOGIN_METHOD_IDZIENNIK_WEB) {
IdziennikWebGetAttachment(data, message, attachmentId, attachmentName) { IdziennikWebGetAttachment(data, owner, attachmentId, attachmentName) {
completed() completed()
} }
} }

View File

@ -15,7 +15,7 @@ import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File import java.io.File
class IdziennikWebGetAttachment(override val data: DataIdziennik, class IdziennikWebGetAttachment(override val data: DataIdziennik,
val message: Message, val owner: Any,
val attachmentId: Long, val attachmentId: Long,
val attachmentName: String, val attachmentName: String,
val onSuccess: () -> Unit val onSuccess: () -> Unit
@ -25,6 +25,8 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik,
} }
init { init {
val message = owner as Message
val messageId = "\\[META:([A-z0-9]+);([0-9-]+)]".toRegex().find(message.body ?: "")?.get(2) ?: -1 val messageId = "\\[META:([A-z0-9]+);([0-9-]+)]".toRegex().find(message.body ?: "")?.get(2) ?: -1
val targetFile = File(Utils.getStorageDir(), attachmentName) val targetFile = File(Utils.getStorageDir(), attachmentName)
@ -34,13 +36,13 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik,
), { file -> ), { file ->
val event = AttachmentGetEvent( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
AttachmentGetEvent.TYPE_FINISHED, AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath 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) Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event) EventBus.getDefault().post(event)
@ -50,7 +52,7 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik,
}) { written, _ -> }) { written, _ ->
val event = AttachmentGetEvent( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
AttachmentGetEvent.TYPE_PROGRESS, AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written bytesWritten = written

View File

@ -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.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore 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.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull 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) { login(LOGIN_METHOD_LIBRUS_MESSAGES) {
LibrusMessagesGetAttachment(data, message, attachmentId, attachmentName) { LibrusMessagesGetAttachment(data, owner, attachmentId, attachmentName) {
completed() completed()
} }
} }

View File

@ -21,7 +21,7 @@ import java.io.File
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class LibrusMessagesGetAttachment(override val data: DataLibrus, class LibrusMessagesGetAttachment(override val data: DataLibrus,
val message: Message, val owner: Any,
val attachmentId: Long, val attachmentId: Long,
val attachmentName: String, val attachmentName: String,
val onSuccess: () -> Unit val onSuccess: () -> Unit
@ -38,6 +38,8 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus,
private var getAttachmentCheckKeyTries = 0 private var getAttachmentCheckKeyTries = 0
init { init {
val message = owner as Message
messagesGet(TAG, "GetFileDownloadLink", parameters = mapOf( messagesGet(TAG, "GetFileDownloadLink", parameters = mapOf(
"fileId" to attachmentId, "fileId" to attachmentId,
"msgId" to message.id, "msgId" to message.id,
@ -95,13 +97,13 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus,
val event = AttachmentGetEvent( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
TYPE_FINISHED, TYPE_FINISHED,
file.absolutePath 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) Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event) EventBus.getDefault().post(event)
@ -111,7 +113,7 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus,
}) { written, _ -> }) { written, _ ->
val event = AttachmentGetEvent( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
TYPE_PROGRESS, TYPE_PROGRESS,
bytesWritten = written bytesWritten = written

View File

@ -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.interfaces.EdziennikInterface
import pl.szczodrzynski.edziennik.data.api.models.ApiError import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore 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.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.utils.Utils.d 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 { class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginStore, val callback: EdziennikCallback) : EdziennikInterface {
companion object { companion object {
@ -104,9 +102,9 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
override fun markAllAnnouncementsAsRead() {} override fun markAllAnnouncementsAsRead() {}
override fun getAnnouncement(announcement: AnnouncementFull) {} 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) { login(LOGIN_METHOD_MOBIDZIENNIK_WEB) {
MobidziennikWebGetAttachment(data, message, attachmentId, attachmentName) { MobidziennikWebGetAttachment(data, owner, attachmentId, attachmentName) {
completed() completed()
} }
} }
@ -121,13 +119,8 @@ class Mobidziennik(val app: App, val profile: Profile?, val loginStore: LoginSto
} }
override fun getEvent(eventFull: EventFull) { override fun getEvent(eventFull: EventFull) {
val type = if (eventFull.date >= Date.getToday())
MobidziennikWebHomework.TYPE_CURRENT
else
MobidziennikWebHomework.TYPE_PAST
login(LOGIN_METHOD_MOBIDZIENNIK_WEB) { login(LOGIN_METHOD_MOBIDZIENNIK_WEB) {
MobidziennikWebHomework(data, 0L, type, eventFull) { MobidziennikWebGetHomework(data, eventFull) {
completed() completed()
} }
} }

View File

@ -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.DataMobidziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb import pl.szczodrzynski.edziennik.data.api.edziennik.mobidziennik.data.MobidziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent 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.data.db.entity.Message
import pl.szczodrzynski.edziennik.utils.Utils import pl.szczodrzynski.edziennik.utils.Utils
import pl.szczodrzynski.edziennik.utils.models.Date
import java.io.File import java.io.File
class MobidziennikWebGetAttachment(override val data: DataMobidziennik, class MobidziennikWebGetAttachment(override val data: DataMobidziennik,
val message: Message, val owner: Any,
val attachmentId: Long, val attachmentId: Long,
val attachmentName: String, val attachmentName: String,
val onSuccess: () -> Unit val onSuccess: () -> Unit
@ -25,22 +27,37 @@ class MobidziennikWebGetAttachment(override val data: DataMobidziennik,
init { init {
val targetFile = File(Utils.getStorageDir(), attachmentName) val targetFile = File(Utils.getStorageDir(), attachmentName)
val typeUrl = if (message.type == Message.TYPE_SENT) val typeUrl = when (owner) {
"wiadwyslana" is Message -> if (owner.type == Message.TYPE_SENT)
"dziennik/wiadwyslana?id="
else else
"wiadodebrana" "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( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
AttachmentGetEvent.TYPE_FINISHED, AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath 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) Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event) EventBus.getDefault().post(event)
@ -51,7 +68,7 @@ class MobidziennikWebGetAttachment(override val data: DataMobidziennik,
// TODO make use of bytesTotal // TODO make use of bytesTotal
val event = AttachmentGetEvent( val event = AttachmentGetEvent(
profileId, profileId,
message.id, owner,
attachmentId, attachmentId,
AttachmentGetEvent.TYPE_PROGRESS, AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written bytesWritten = written

View File

@ -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()
}
}
}

View File

@ -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.prepare
import pl.szczodrzynski.edziennik.data.api.templateLoginMethods import pl.szczodrzynski.edziennik.data.api.templateLoginMethods
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore 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.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull 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) {
} }

View File

@ -19,7 +19,6 @@ import pl.szczodrzynski.edziennik.data.api.prepare
import pl.szczodrzynski.edziennik.data.api.prepareFor import pl.szczodrzynski.edziennik.data.api.prepareFor
import pl.szczodrzynski.edziennik.data.api.vulcanLoginMethods import pl.szczodrzynski.edziennik.data.api.vulcanLoginMethods
import pl.szczodrzynski.edziennik.data.db.entity.LoginStore 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.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull 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 markAllAnnouncementsAsRead() {}
override fun getAnnouncement(announcement: AnnouncementFull) {} 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 getRecipientList() {}
override fun getEvent(eventFull: EventFull) {} override fun getEvent(eventFull: EventFull) {}

View File

@ -4,11 +4,21 @@
package pl.szczodrzynski.edziennik.data.api.events 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, var eventType: Int = TYPE_PROGRESS, val fileName: String? = null,
val bytesWritten: Long = 0) { val bytesWritten: Long = 0) {
companion object { companion object {
const val TYPE_PROGRESS = 0 const val TYPE_PROGRESS = 0
const val TYPE_FINISHED = 1 const val TYPE_FINISHED = 1
} }
val ownerId
get() = when (owner) {
is Message -> owner.id
is Event -> owner.id
else -> -1
}
} }

View File

@ -5,7 +5,6 @@
package pl.szczodrzynski.edziennik.data.api.interfaces package pl.szczodrzynski.edziennik.data.api.interfaces
import com.google.gson.JsonObject 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.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull import pl.szczodrzynski.edziennik.data.db.full.EventFull
@ -17,7 +16,7 @@ interface EdziennikInterface {
fun sendMessage(recipients: List<Teacher>, subject: String, text: String) fun sendMessage(recipients: List<Teacher>, subject: String, text: String)
fun markAllAnnouncementsAsRead() fun markAllAnnouncementsAsRead()
fun getAnnouncement(announcement: AnnouncementFull) fun getAnnouncement(announcement: AnnouncementFull)
fun getAttachment(message: Message, attachmentId: Long, attachmentName: String) fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String)
fun getRecipientList() fun getRecipientList()
fun getEvent(eventFull: EventFull) fun getEvent(eventFull: EventFull)
fun firstLogin() fun firstLogin()

View File

@ -61,6 +61,7 @@ class EventDetailsDialog(
if (activity.isFinishing) if (activity.isFinishing)
return@run return@run
onShowListener?.invoke(TAG) onShowListener?.invoke(TAG)
EventBus.getDefault().register(this)
app = activity.applicationContext as App app = activity.applicationContext as App
b = DialogEventDetailsBinding.inflate(activity.layoutInflater) b = DialogEventDetailsBinding.inflate(activity.layoutInflater)
dialog = MaterialAlertDialogBuilder(activity) dialog = MaterialAlertDialogBuilder(activity)
@ -74,6 +75,7 @@ class EventDetailsDialog(
} }
.setOnDismissListener { .setOnDismissListener {
onDismissListener?.invoke(TAG) onDismissListener?.invoke(TAG)
EventBus.getDefault().unregister(this@EventDetailsDialog)
progressDialog?.dismiss() progressDialog?.dismiss()
} }
.show() .show()
@ -197,11 +199,18 @@ class EventDetailsDialog(
} }
if (event.homeworkBody == null && !event.addedManually && event.type == Event.TYPE_HOMEWORK) { if (event.homeworkBody == null && !event.addedManually && event.type == Event.TYPE_HOMEWORK) {
b.bodyTitle.isVisible = true
b.bodyProgressBar.isVisible = true b.bodyProgressBar.isVisible = true
b.body.isVisible = false b.body.isVisible = false
EdziennikTask.eventGet(event.profileId, event).enqueue(activity) 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 { else {
b.bodyTitle.isVisible = true
b.bodyProgressBar.isVisible = false b.bodyProgressBar.isVisible = false
b.body.isVisible = true b.body.isVisible = true
b.body.text = event.homeworkBody b.body.text = event.homeworkBody
@ -209,6 +218,20 @@ class EventDetailsDialog(
dialog.dismiss() 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) @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)

View File

@ -375,7 +375,7 @@ class MessageFragment : Fragment(), CoroutineScope {
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
fun onAttachmentGetEvent(event: AttachmentGetEvent) { fun onAttachmentGetEvent(event: AttachmentGetEvent) {
attachmentList.firstOrNull { it.profileId == event.profileId attachmentList.firstOrNull { it.profileId == event.profileId
&& it.messageId == event.messageId && it.messageId == event.ownerId
&& it.attachmentId == event.attachmentId }?.let { attachment -> && it.attachmentId == event.attachmentId }?.let { attachment ->
when (event.eventType) { when (event.eventType) {

View File

@ -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<AttachmentAdapter.ViewHolder>(), 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<Item>()
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)
}

View File

@ -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<Int>("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)
}
}

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Kuba Szczodrzyński 2020-4-1.
-->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="8dp">
<com.google.android.material.chip.Chip
android:id="@+id/chip"
android:layout_width="match_parent"
android:layout_height="48dp"
android:ellipsize="middle"
app:chipIconSize="24dp"
app:chipIconVisible="true"
app:closeIconSize="18dp"
app:closeIconVisible="false"
tools:chipIcon="@android:drawable/ic_menu_delete"
tools:closeIcon="@drawable/ic_account_circle"
tools:closeIconVisible="true"
tools:text="Zadanie z matematyki.pdf" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_gravity="end|center_vertical"
android:layout_marginHorizontal="8dp" />
</FrameLayout>
</layout>

View File

@ -152,12 +152,12 @@
tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia." /> tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia." />
<TextView <TextView
android:id="@+id/bodyTitle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:textAppearance="@style/NavView.TextView.Helper"
android:text="@string/dialog_event_details_body" android:text="@string/dialog_event_details_body"
android:visibility="@{event.homeworkBody != null ? View.VISIBLE : View.GONE}"/> android:textAppearance="@style/NavView.TextView.Helper" />
<ProgressBar <ProgressBar
android:id="@+id/bodyProgressBar" android:id="@+id/bodyProgressBar"
@ -175,9 +175,21 @@
android:text="@{event.homeworkBody}" android:text="@{event.homeworkBody}"
android:textAppearance="@style/NavView.TextView.Medium" android:textAppearance="@style/NavView.TextView.Medium"
android:textIsSelectable="true" android:textIsSelectable="true"
android:visibility="@{event.homeworkBody != null ? View.VISIBLE : View.GONE}"
tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia." /> tools:text="Rozdział II: Panowanie Piastów i Jagiellonów.Przeniesiony z 11 grudnia." />
<TextView
android:id="@+id/attachmentsTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/dialog_event_details_attachments"
android:textAppearance="@style/NavView.TextView.Helper" />
<pl.szczodrzynski.edziennik.ui.modules.views.AttachmentsView
android:id="@+id/attachmentsFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -1284,4 +1284,5 @@
<string name="event_mark_as_done_title">Oznacz jako wykonane</string> <string name="event_mark_as_done_title">Oznacz jako wykonane</string>
<string name="event_mark_as_done_text">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.</string> <string name="event_mark_as_done_text">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.</string>
<string name="dialog_event_details_body">Treść</string> <string name="dialog_event_details_body">Treść</string>
<string name="dialog_event_details_attachments">Załączniki</string>
</resources> </resources>