[API] Implement Idziennik homework attachment downloading. Change attachment events to sticky.

This commit is contained in:
Kuba Szczodrzyński 2020-04-01 21:49:05 +02:00
parent 9303483470
commit 12d8de1def
13 changed files with 209 additions and 23 deletions

View File

@ -74,6 +74,8 @@ const val IDZIENNIK_WEB_GET_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/Pobier
const val IDZIENNIK_WEB_GET_RECIPIENT_LIST = "mod_komunikator/WS_wiadomosci.asmx/pobierzListeOdbiorcowPanelRodzic"
const val IDZIENNIK_WEB_SEND_MESSAGE = "mod_komunikator/WS_wiadomosci.asmx/WyslijWiadomosc"
const val IDZIENNIK_WEB_GET_ATTACHMENT = "mod_komunikator/Download.ashx"
const val IDZIENNIK_WEB_GET_HOMEWORK = "mod_panelRodzica/pracaDomowa/WS_pracaDomowa.asmx/pobierzJednaPraceDomowa"
const val IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT = "mod_panelRodzica/pracaDomowa.aspx"
val IDZIENNIK_API_USER_AGENT = SYSTEM_USER_AGENT
const val IDZIENNIK_API_URL = "https://iuczniowie.progman.pl/idziennik/api"

View File

@ -8,18 +8,13 @@ import com.google.gson.JsonObject
import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.data.api.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikData
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetAttachment
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebGetRecipientList
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.IdziennikWebSendMessage
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web.*
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.firstlogin.IdziennikFirstLogin
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.login.IdziennikLogin
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.Profile
import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.entity.*
import pl.szczodrzynski.edziennik.data.db.full.AnnouncementFull
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
@ -105,8 +100,15 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
override fun getAttachment(owner: Any, attachmentId: Long, attachmentName: String) {
login(LOGIN_METHOD_IDZIENNIK_WEB) {
IdziennikWebGetAttachment(data, owner, attachmentId, attachmentName) {
completed()
if (owner is Message) {
IdziennikWebGetAttachment(data, owner, attachmentId, attachmentName) {
completed()
}
}
else if (owner is Event) {
IdziennikWebGetHomeworkAttachment(data, owner, attachmentId, attachmentName) {
completed()
}
}
}
}
@ -119,7 +121,13 @@ class Idziennik(val app: App, val profile: Profile?, val loginStore: LoginStore,
}
}
override fun getEvent(eventFull: EventFull) {}
override fun getEvent(eventFull: EventFull) {
login(LOGIN_METHOD_IDZIENNIK_WEB) {
IdziennikWebGetHomework(data, eventFull) {
completed()
}
}
}
override fun firstLogin() { IdziennikFirstLogin(data) { completed() } }
override fun cancel() {

View File

@ -229,6 +229,7 @@ open class IdziennikWeb(open val data: DataIdziennik, open val lastSync: Long?)
.apply {
parameters.forEach { (k, v) -> addParameter(k, v) }
}
.contentType("application/x-www-form-urlencoded")
.post()
.callback(callback)
.build()

View File

@ -45,7 +45,7 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik,
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event)
EventBus.getDefault().postSticky(event)
onSuccess()
@ -58,7 +58,7 @@ class IdziennikWebGetAttachment(override val data: DataIdziennik,
bytesWritten = written
)
EventBus.getDefault().post(event)
EventBus.getDefault().postSticky(event)
}
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-1.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_HOMEWORK
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.EventGetEvent
import pl.szczodrzynski.edziennik.data.api.models.ApiError
import pl.szczodrzynski.edziennik.data.db.full.EventFull
import pl.szczodrzynski.edziennik.getBoolean
import pl.szczodrzynski.edziennik.getJsonObject
import pl.szczodrzynski.edziennik.getString
class IdziennikWebGetHomework(override val data: DataIdziennik,
val event: EventFull,
val onSuccess: () -> Unit
) : IdziennikWeb(data, null) {
companion object {
private const val TAG = "IdziennikWebGetHomework"
}
init {
webApiGet(TAG, IDZIENNIK_WEB_GET_HOMEWORK, mapOf(
"idP" to data.registerId,
"idPD" to event.id
)) { result ->
val json = result.getJsonObject("d") ?: run {
data.error(ApiError(TAG, ERROR_IDZIENNIK_WEB_REQUEST_NO_DATA)
.withApiResponse(result))
return@webApiGet
}
val homework = json.getJsonObject("praca") ?: return@webApiGet
if (homework.getBoolean("zalacznik", false)) {
event.attachmentIds = mutableListOf(event.id)
event.attachmentNames = mutableListOf("Załącznik do zadania")
}
else {
event.attachmentIds = mutableListOf()
event.attachmentNames = mutableListOf()
}
event.homeworkBody = homework.getString("tresc")
data.eventList.add(event)
data.eventListReplace = true
EventBus.getDefault().postSticky(EventGetEvent(event))
onSuccess()
}
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) Kuba Szczodrzyński 2020-4-1.
*/
package pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.web
import com.google.gson.JsonObject
import org.greenrobot.eventbus.EventBus
import pl.szczodrzynski.edziennik.data.api.IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT
import pl.szczodrzynski.edziennik.data.api.Regexes
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.DataIdziennik
import pl.szczodrzynski.edziennik.data.api.edziennik.idziennik.data.IdziennikWeb
import pl.szczodrzynski.edziennik.data.api.events.AttachmentGetEvent
import pl.szczodrzynski.edziennik.data.db.entity.Event
import pl.szczodrzynski.edziennik.get
import pl.szczodrzynski.edziennik.getString
import pl.szczodrzynski.edziennik.set
import pl.szczodrzynski.edziennik.utils.Utils
import java.io.File
class IdziennikWebGetHomeworkAttachment(override val data: DataIdziennik,
val owner: Any,
val attachmentId: Long,
val attachmentName: String,
val onSuccess: () -> Unit
) : IdziennikWeb(data, null) {
companion object {
const val TAG = "IdziennikWebGetHomeworkAttachment"
}
init {
val homework = owner as Event
/*val request = Request.Builder()
.url("")
.build()
data.app.http.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
data.error(ApiError(TAG, ERROR_REQUEST_FAILURE)
.withThrowable(e))
}
override fun onResponse(call: Call, response: Response) {
val filename = response.header("content-disposition")?.substringAfter("\"")?.substringBeforeLast("\"")
val file: File = File(Utils.getStorageDir(), filename)
val sink = file.sink().buffer()
response.body()?.source()?.let {
sink.writeAll(it)
}
sink.close()
}
})*/
webGet(TAG, IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT) { text ->
val hiddenFields = JsonObject()
Regexes.IDZIENNIK_LOGIN_HIDDEN_FIELDS.findAll(text).forEach {
hiddenFields[it[1]] = it[2]
}
webGetFile(TAG, IDZIENNIK_WEB_GET_HOMEWORK_ATTACHMENT, Utils.getStorageDir(), mapOf(
"__VIEWSTATE" to hiddenFields.getString("__VIEWSTATE", ""),
"__VIEWSTATEGENERATOR" to hiddenFields.getString("__VIEWSTATEGENERATOR", ""),
"__EVENTVALIDATION" to hiddenFields.getString("__EVENTVALIDATION", ""),
"__EVENTTARGET" to "ctl00\$cphContent\$bt_pobraniePliku",
"ctl00\$dxComboUczniowie" to data.registerId,
"ctl00\$cphContent\$idPracyDomowej" to attachmentId
), { file ->
val event = AttachmentGetEvent(
profileId,
owner,
attachmentId,
AttachmentGetEvent.TYPE_FINISHED,
file.absolutePath
)
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
homework.attachmentNames = mutableListOf(file.name)
data.eventList.add(homework)
data.eventListReplace = true
EventBus.getDefault().postSticky(event)
onSuccess()
}) { written, _ ->
val event = AttachmentGetEvent(
profileId,
owner,
attachmentId,
AttachmentGetEvent.TYPE_PROGRESS,
bytesWritten = written
)
EventBus.getDefault().postSticky(event)
}
}
}
}

View File

@ -52,6 +52,7 @@ class IdziennikWebHomework(override val data: DataIdziennik,
json.getJsonArray("ListK")?.asJsonObjectList()?.forEach { homework ->
val id = homework.getLong("_recordId") ?: return@forEach
val eventDate = Date.fromY_m_d(homework.getString("dataO") ?: return@forEach)
val addedDate = Date.fromY_m_d(homework.getString("dataZ") ?: return@forEach)
val subjectName = homework.getString("przed") ?: return@forEach
val subjectId = data.getSubject(subjectName, null, subjectName).id
val teacherName = homework.getString("usr") ?: return@forEach
@ -86,7 +87,7 @@ class IdziennikWebHomework(override val data: DataIdziennik,
eventObject.id,
seen,
seen,
System.currentTimeMillis()
addedDate.inMillis
))
}

View File

@ -106,7 +106,7 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus,
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event)
EventBus.getDefault().postSticky(event)
onSuccess()
@ -119,7 +119,7 @@ class LibrusMessagesGetAttachment(override val data: DataLibrus,
bytesWritten = written
)
EventBus.getDefault().post(event)
EventBus.getDefault().postSticky(event)
}
}
}

View File

@ -60,7 +60,7 @@ class MobidziennikWebGetAttachment(override val data: DataMobidziennik,
val attachmentDataFile = File(Utils.getStorageDir(), ".${profileId}_${event.ownerId}_${event.attachmentId}")
Utils.writeStringToFile(attachmentDataFile, event.fileName)
EventBus.getDefault().post(event)
EventBus.getDefault().postSticky(event)
onSuccess()
@ -74,7 +74,7 @@ class MobidziennikWebGetAttachment(override val data: DataMobidziennik,
bytesWritten = written
)
EventBus.getDefault().post(event)
EventBus.getDefault().postSticky(event)
}
}
}

View File

@ -372,8 +372,9 @@ class MessageFragment : Fragment(), CoroutineScope {
).enqueue(activity)
}
@Subscribe(threadMode = ThreadMode.MAIN)
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onAttachmentGetEvent(event: AttachmentGetEvent) {
EventBus.getDefault().removeStickyEvent(event)
attachmentList.firstOrNull { it.profileId == event.profileId
&& it.messageId == event.ownerId
&& it.attachmentId == event.attachmentId }?.let { attachment ->

View File

@ -14,6 +14,7 @@ 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.paddingDp
import com.mikepenz.iconics.utils.sizeDp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -84,6 +85,7 @@ class AttachmentAdapter(
.icon(icon)
.colorAttr(context, R.attr.colorOnSurface)
.sizeDp(24)
.paddingDp(2)
b.chip.closeIcon = IconicsDrawable(context)
.icon(CommunityMaterial.Icon.cmd_check)
.colorAttr(context, R.attr.colorOnSurface)
@ -106,7 +108,7 @@ class AttachmentAdapter(
val profileId: Int,
val owner: Any,
val id: Long,
val name: String,
var name: String,
var size: Long?
) {
val ownerId

View File

@ -97,7 +97,7 @@ class AttachmentsView @JvmOverloads constructor(
private fun downloadAttachment(attachment: AttachmentAdapter.Item, forceDownload: Boolean = false) {
if (!forceDownload && attachment.isDownloaded) {
Utils.openFile(context, File(attachment.downloadedName))
Utils.openFile(context, File(Utils.getStorageDir(), attachment.name))
return
}
@ -115,8 +115,9 @@ class AttachmentsView @JvmOverloads constructor(
}
private val lastUpdate: Long = 0
@Subscribe(threadMode = ThreadMode.MAIN)
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onAttachmentGetEvent(event: AttachmentGetEvent) {
EventBus.getDefault().removeStickyEvent(event)
val attachment = (adapter as? AttachmentAdapter)?.items?.firstOrNull {
it.profileId == event.profileId
&& it.owner == event.owner
@ -131,8 +132,13 @@ class AttachmentsView @JvmOverloads constructor(
attachment.isDownloading = false
attachment.isDownloaded = true
// update file name for iDziennik which
// does not provide the name before downloading
if (!attachment.name.contains("."))
attachment.name = File(attachment.downloadedName).name
// open the file
Utils.openFile(context, File(attachment.downloadedName))
Utils.openFile(context, File(Utils.getStorageDir(), attachment.name))
}
AttachmentGetEvent.TYPE_PROGRESS -> {

View File

@ -19,7 +19,7 @@ import okhttp3.internal.Util;
* <p>Time : 下午2:39.</p>
*/
public class FileCallbackHandler extends AbsCallbackHandler<File> {
final private File file;
private File file;
final private static int BUFFER_SIZE = 4096;
public FileCallbackHandler(Context context){
@ -66,6 +66,14 @@ public class FileCallbackHandler extends AbsCallbackHandler<File> {
if (file == null){
throw new IllegalArgumentException("File == null");
}
if (this.file.isDirectory()) {
String contentDisposition = response.header("content-disposition");
if (contentDisposition != null) {
String filename = contentDisposition.substring(contentDisposition.indexOf("\"")+1, contentDisposition.lastIndexOf("\""));
this.file = new File(file, filename);
file = this.file;
}
}
InputStream instream = response.body().byteStream();
long contentLength = response.body().contentLength();
FileOutputStream buffer = new FileOutputStream(file);